/[debian]/bti/trunk/bti.c
ViewVC logotype

Contents of /bti/trunk/bti.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1503 - (show annotations)
Sat Jan 10 04:53:56 2009 UTC (11 years, 9 months ago) by gregoa
File MIME type: text/plain
File size: 10466 byte(s)
New upstream release (due to december twitter server change).
1 /*
2 * Copyright (C) 2008 Greg Kroah-Hartman <greg@kroah.com>
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation version 2 of the License.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 */
17
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <stddef.h>
21 #include <string.h>
22 #include <getopt.h>
23 #include <errno.h>
24 #include <ctype.h>
25 #include <fcntl.h>
26 #include <unistd.h>
27 #include <sys/stat.h>
28 #include <curl/curl.h>
29 #include "bti_version.h"
30
31
32 #define zalloc(size) calloc(size, 1)
33
34 #define dbg(format, arg...) \
35 do { \
36 if (debug) \
37 printf("%s: " format , __func__ , ## arg ); \
38 } while (0)
39
40
41 static int debug = 0;
42
43 enum host {
44 HOST_TWITTER = 0,
45 HOST_IDENTICA = 1,
46 };
47
48 struct session {
49 char *password;
50 char *account;
51 char *tweet;
52 int bash;
53 enum host host;
54 };
55
56 struct bti_curl_buffer {
57 char *data;
58 int length;
59 };
60
61 static void display_help(void)
62 {
63 fprintf(stdout, "bti - send tweet to twitter\n");
64 fprintf(stdout, "Version: " BTI_VERSION "\n");
65 fprintf(stdout, "Usage:\n");
66 fprintf(stdout, " bti [options]\n");
67 fprintf(stdout, "options are:\n");
68 fprintf(stdout, " --account accountname\n");
69 fprintf(stdout, " --password password\n");
70 fprintf(stdout, " --host HOST\n");
71 fprintf(stdout, " --bash\n");
72 fprintf(stdout, " --debug\n");
73 fprintf(stdout, " --version\n");
74 fprintf(stdout, " --help\n");
75 }
76
77 static void display_version(void)
78 {
79 fprintf(stdout, "bti - version %s\n", BTI_VERSION);
80 }
81
82 static char *get_string_from_stdin(void)
83 {
84 char *temp;
85 char *string;
86
87 string = zalloc(1000);
88 if (!string)
89 return NULL;
90
91 if (!fgets(string, 999, stdin))
92 return NULL;
93 temp = strchr(string, '\n');
94 *temp = '\0';
95 return string;
96 }
97
98 static struct session *session_alloc(void)
99 {
100 struct session *session;
101
102 session = zalloc(sizeof(*session));
103 if (!session)
104 return NULL;
105 return session;
106 }
107
108 static void session_free(struct session *session)
109 {
110 if (!session)
111 return;
112 free(session->password);
113 free(session->account);
114 free(session->tweet);
115 free(session);
116 }
117
118 static struct bti_curl_buffer *bti_curl_buffer_alloc(void)
119 {
120 struct bti_curl_buffer *buffer;
121
122 buffer = zalloc(sizeof(*buffer));
123 if (!buffer)
124 return NULL;
125
126 /* start out with a data buffer of 1 byte to
127 * make the buffer fill logic simpler */
128 buffer->data = zalloc(1);
129 if (!buffer->data) {
130 free(buffer);
131 return NULL;
132 }
133 buffer->length = 0;
134 return buffer;
135 }
136
137 static void bti_curl_buffer_free(struct bti_curl_buffer *buffer)
138 {
139 if (!buffer)
140 return;
141 free(buffer->data);
142 free(buffer);
143 }
144
145 static const char *twitter_url = "https://twitter.com/statuses/update.xml";
146 static const char *identica_url = "http://identi.ca/api/statuses/update.xml";
147
148 static CURL *curl_init(void)
149 {
150 CURL *curl;
151
152 curl = curl_easy_init();
153 if (!curl) {
154 fprintf(stderr, "Can not init CURL!\n");
155 return NULL;
156 }
157 /* some ssl sanity checks on the connection we are making */
158 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
159 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
160 return curl;
161 }
162
163 size_t curl_callback(void *buffer, size_t size, size_t nmemb, void *userp)
164 {
165 struct bti_curl_buffer *curl_buf = userp;
166 size_t buffer_size = size * nmemb;
167 char *temp;
168
169 if ((!buffer) || (!buffer_size) || (!curl_buf))
170 return -EINVAL;
171
172 /* add to the data we already have */
173 temp = zalloc(curl_buf->length + buffer_size + 1);
174 if (!temp)
175 return -ENOMEM;
176
177 memcpy(temp, curl_buf->data, curl_buf->length);
178 free(curl_buf->data);
179 curl_buf->data = temp;
180 memcpy(&curl_buf->data[curl_buf->length], (char *)buffer, buffer_size);
181 curl_buf->length += buffer_size;
182
183 dbg("%s\n", curl_buf->data);
184
185 return buffer_size;
186 }
187
188 static int send_tweet(struct session *session)
189 {
190 char user_password[500];
191 char data[500];
192 struct bti_curl_buffer *curl_buf;
193 CURL *curl = NULL;
194 CURLcode res;
195 struct curl_httppost *formpost = NULL;
196 struct curl_httppost *lastptr = NULL;
197 struct curl_slist *slist = NULL;
198
199 if (!session)
200 return -EINVAL;
201
202 curl_buf = bti_curl_buffer_alloc();
203 if (!curl_buf)
204 return -ENOMEM;
205
206 snprintf(user_password, sizeof(user_password), "%s:%s",
207 session->account, session->password);
208 snprintf(data, sizeof(data), "status=\"%s\"", session->tweet);
209
210 curl = curl_init();
211 if (!curl)
212 return -EINVAL;
213
214 curl_formadd(&formpost, &lastptr,
215 CURLFORM_COPYNAME, "status",
216 CURLFORM_COPYCONTENTS, session->tweet,
217 CURLFORM_END);
218
219 curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
220
221 switch (session->host) {
222 case HOST_TWITTER:
223 curl_easy_setopt(curl, CURLOPT_URL, twitter_url);
224 /*
225 * twitter doesn't like the "Expect: 100-continue" header
226 * anymore, so turn it off.
227 */
228 slist = curl_slist_append(slist, "Expect:");
229 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);
230 break;
231 case HOST_IDENTICA:
232 curl_easy_setopt(curl, CURLOPT_URL, identica_url);
233 break;
234 }
235
236 if (debug)
237 curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
238 curl_easy_setopt(curl, CURLOPT_USERPWD, user_password);
239
240 dbg("user_password = %s\n", user_password);
241 dbg("data = %s\n", data);
242
243 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_callback);
244 curl_easy_setopt(curl, CURLOPT_WRITEDATA, curl_buf);
245 res = curl_easy_perform(curl);
246 if (res && !session->bash) {
247 fprintf(stderr, "error(%d) trying to send tweet\n", res);
248 return -EINVAL;
249 }
250
251 curl_easy_cleanup(curl);
252 curl_formfree(formpost);
253 bti_curl_buffer_free(curl_buf);
254 return 0;
255 }
256
257 static void parse_configfile(struct session *session)
258 {
259 FILE *config_file;
260 char *line = NULL;
261 size_t len = 0;
262 char *account = NULL;
263 char *password = NULL;
264 char *host = NULL;
265 char *file;
266 char *home = getenv("HOME");
267
268 /* config file is ~/.bti */
269 file = alloca(strlen(home) + 7);
270
271 sprintf(file, "%s/.bti", home);
272
273 config_file = fopen(file, "r");
274
275 /* No error if file does not exist or is unreadable. */
276 if (config_file == NULL)
277 return;
278
279 do {
280 ssize_t n = getline(&line, &len, config_file);
281 if (n < 0)
282 break;
283 if (line[n - 1] == '\n')
284 line[n - 1] = '\0';
285 /* Parse file. Format is the usual value pairs:
286 account=name
287 passwort=value
288 # is a comment character
289 */
290 *strchrnul(line, '#') = '\0';
291 char *c = line;
292 while (isspace(*c))
293 c++;
294 /* Ignore blank lines. */
295 if (c[0] == '\0')
296 continue;
297
298 if (!strncasecmp(c, "account", 7) && (c[7] == '=')) {
299 c += 8;
300 if (c[0] != '\0')
301 account = strdup(c);
302 } else if (!strncasecmp(c, "password", 8) &&
303 (c[8] == '=')) {
304 c += 9;
305 if (c[0] != '\0')
306 password = strdup(c);
307 } else if (!strncasecmp(c, "host", 4) &&
308 (c[4] == '=')) {
309 c += 5;
310 if (c[0] != '\0')
311 host = strdup(c);
312 }
313 } while (!feof(config_file));
314
315 if (password)
316 session->password = password;
317 if (account)
318 session->account = account;
319 if (host) {
320 if (strcasecmp(host, "twitter") == 0)
321 session->host = HOST_TWITTER;
322 if (strcasecmp(host, "identica") == 0)
323 session->host = HOST_IDENTICA;
324 free(host);
325 }
326
327 /* Free buffer and close file. */
328 free(line);
329 fclose(config_file);
330 }
331
332 int main(int argc, char *argv[], char *envp[])
333 {
334 static const struct option options[] = {
335 { "debug", 0, NULL, 'd' },
336 { "account", 1, NULL, 'a' },
337 { "password", 1, NULL, 'p' },
338 { "host", 1, NULL, 'H' },
339 { "help", 0, NULL, 'h' },
340 { "bash", 0, NULL, 'b' },
341 { "version", 0, NULL, 'v' },
342 { }
343 };
344 struct session *session;
345 pid_t child;
346 char *tweet;
347 int retval;
348 int option;
349 #if 0
350 char *home = getenv("HOME");
351 char *pwd = getenv("PWD");
352 char *dir;
353 #endif
354 session = session_alloc();
355 if (!session) {
356 fprintf(stderr, "no more memory...\n");
357 return -1;
358 }
359
360 curl_global_init(CURL_GLOBAL_ALL);
361 parse_configfile(session);
362
363 while (1) {
364 option = getopt_long_only(argc, argv, "dqe:p:H:a:h",
365 options, NULL);
366 if (option == -1)
367 break;
368 switch (option) {
369 case 'd':
370 debug = 1;
371 break;
372 case 'a':
373 if (session->account)
374 free(session->account);
375 session->account = strdup(optarg);
376 dbg("account = %s\n", session->account);
377 break;
378 case 'p':
379 if (session->password)
380 free(session->password);
381 session->password = strdup(optarg);
382 dbg("password = %s\n", session->password);
383 break;
384 case 'H':
385 if (strcasecmp(optarg, "twitter") == 0)
386 session->host = HOST_TWITTER;
387 if (strcasecmp(optarg, "identica") == 0)
388 session->host = HOST_IDENTICA;
389 dbg("host = %d\n", session->host);
390 break;
391 case 'b':
392 session->bash= 1;
393 break;
394 case 'h':
395 display_help();
396 goto exit;
397 case 'v':
398 display_version();
399 goto exit;
400 default:
401 display_help();
402 goto exit;
403 }
404 }
405
406 if (!session->account) {
407 fprintf(stdout, "Enter twitter account: ");
408 session->account = get_string_from_stdin();
409 }
410
411 if (!session->password) {
412 fprintf(stdout, "Enter twitter password: ");
413 session->password = get_string_from_stdin();
414 }
415 #if 0
416 /* get the current working directory basename */
417 if (strcmp(pwd, home) == 0)
418 dir = "~";
419 else {
420 dir = strrchr(pwd, '/');
421 if (dir)
422 dir++;
423 else
424 dir = "?";
425 }
426 #endif
427 tweet = get_string_from_stdin();
428 if (!tweet || strlen(tweet) == 0) {
429 dbg("no tweet?\n");
430 return -1;
431 }
432
433 // session->tweet = zalloc(strlen(tweet) + strlen(dir) + 10);
434 session->tweet = zalloc(strlen(tweet) + 10);
435
436 /* if --bash is specified, add the "PWD $ " to
437 * the start of the tweet. */
438 if (session->bash)
439 // sprintf(session->tweet, "%s $ %s", dir, tweet);
440 sprintf(session->tweet, "$ %s", tweet);
441 else
442 sprintf(session->tweet, "%s", tweet);
443 free(tweet);
444
445 dbg("account = %s\n", session->account);
446 dbg("password = %s\n", session->password);
447 dbg("tweet = %s\n", session->tweet);
448 dbg("host = %d\n", session->host);
449
450 /* fork ourself so that the main shell can get on
451 * with it's life as we try to connect and handle everything
452 */
453 if (session->bash) {
454 child = fork();
455 if (child) {
456 dbg("child is %d\n", child);
457 exit(0);
458 }
459 }
460
461 retval = send_tweet(session);
462 if (retval && !session->bash) {
463 fprintf(stderr, "tweet failed\n");
464 return -1;
465 }
466
467 session_free(session);
468 exit:
469 return 0;
470 }

  ViewVC Help
Powered by ViewVC 1.1.26