Changes in / [e1d3f98:b006464]
- Files:
-
- 4 added
- 16 edited
Legend:
- Unmodified
- Added
- Removed
-
debian/copyright
re1d3f98 rb006464 8 8 9 9 Mainly Copyright 2002-2012 Wilmer van der Gaast. 10 Some parts are borrowed from Gaim (version 0.58) <http://gaim.sf.net/>. 11 For the copyrights on those parts, please read the Gaim source code. 10 11 12 Bits of third party code, also GPLed: 13 * Some parts (mostly protocols/oscar/) are borrowed from Gaim (version 14 0.58), now known as Pidgin <http://www.pidgin.im/>. 15 * protocols/yahoo/ is libyahoo2 <http://libyahoo2.sf.net/>. 16 17 Other license (but GPL-compatible): 18 * lib/json.[ch] is from <https://github.com/udp/json-parser> and licensed 19 under the modified BSD license. 20 12 21 13 22 BitlBee License: … … 33 42 34 43 The SGML-formatted documentation is written by Jelmer Vernooij 35 <jelmer@ nl.linux.org> under the GNU Free Documentation License:44 <jelmer@samba.org> under the GNU Free Documentation License: 36 45 37 46 ============================================================================ 38 GNU Free Documentation License 39 Version 1.1, March 2000 47 Copyright (c) 2002-2012 Jelmer Vernooij, Wilmer van der Gaast. 40 48 41 Copyright (C) 2000 Free Software Foundation, Inc. 42 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. 43 Everyone is permitted to copy and distribute verbatim copies 44 of this license document, but changing it is not allowed. 45 46 47 0. PREAMBLE 48 49 The purpose of this License is to make a manual, textbook, or other 50 written document "free" in the sense of freedom: to assure everyone 51 the effective freedom to copy and redistribute it, with or without 52 modifying it, either commercially or noncommercially. Secondarily, 53 this License preserves for the author and publisher a way to get 54 credit for their work, while not being considered responsible for 55 modifications made by others. 56 57 This License is a kind of "copyleft", which means that derivative 58 works of the document must themselves be free in the same sense. It 59 complements the GNU General Public License, which is a copyleft 60 license designed for free software. 61 62 We have designed this License in order to use it for manuals for free 63 software, because free software needs free documentation: a free 64 program should come with manuals providing the same freedoms that the 65 software does. But this License is not limited to software manuals; 66 it can be used for any textual work, regardless of subject matter or 67 whether it is published as a printed book. We recommend this License 68 principally for works whose purpose is instruction or reference. 69 70 71 1. APPLICABILITY AND DEFINITIONS 72 73 This License applies to any manual or other work that contains a 74 notice placed by the copyright holder saying it can be distributed 75 under the terms of this License. The "Document", below, refers to any 76 such manual or work. Any member of the public is a licensee, and is 77 addressed as "you". 78 79 A "Modified Version" of the Document means any work containing the 80 Document or a portion of it, either copied verbatim, or with 81 modifications and/or translated into another language. 82 83 A "Secondary Section" is a named appendix or a front-matter section of 84 the Document that deals exclusively with the relationship of the 85 publishers or authors of the Document to the Document's overall subject 86 (or to related matters) and contains nothing that could fall directly 87 within that overall subject. (For example, if the Document is in part a 88 textbook of mathematics, a Secondary Section may not explain any 89 mathematics.) The relationship could be a matter of historical 90 connection with the subject or with related matters, or of legal, 91 commercial, philosophical, ethical or political position regarding 92 them. 93 94 The "Invariant Sections" are certain Secondary Sections whose titles 95 are designated, as being those of Invariant Sections, in the notice 96 that says that the Document is released under this License. 97 98 The "Cover Texts" are certain short passages of text that are listed, 99 as Front-Cover Texts or Back-Cover Texts, in the notice that says that 100 the Document is released under this License. 101 102 A "Transparent" copy of the Document means a machine-readable copy, 103 represented in a format whose specification is available to the 104 general public, whose contents can be viewed and edited directly and 105 straightforwardly with generic text editors or (for images composed of 106 pixels) generic paint programs or (for drawings) some widely available 107 drawing editor, and that is suitable for input to text formatters or 108 for automatic translation to a variety of formats suitable for input 109 to text formatters. A copy made in an otherwise Transparent file 110 format whose markup has been designed to thwart or discourage 111 subsequent modification by readers is not Transparent. A copy that is 112 not "Transparent" is called "Opaque". 113 114 Examples of suitable formats for Transparent copies include plain 115 ASCII without markup, Texinfo input format, LaTeX input format, SGML 116 or XML using a publicly available DTD, and standard-conforming simple 117 HTML designed for human modification. Opaque formats include 118 PostScript, PDF, proprietary formats that can be read and edited only 119 by proprietary word processors, SGML or XML for which the DTD and/or 120 processing tools are not generally available, and the 121 machine-generated HTML produced by some word processors for output 122 purposes only. 123 124 The "Title Page" means, for a printed book, the title page itself, 125 plus such following pages as are needed to hold, legibly, the material 126 this License requires to appear in the title page. For works in 127 formats which do not have any title page as such, "Title Page" means 128 the text near the most prominent appearance of the work's title, 129 preceding the beginning of the body of the text. 130 131 132 2. VERBATIM COPYING 133 134 You may copy and distribute the Document in any medium, either 135 commercially or noncommercially, provided that this License, the 136 copyright notices, and the license notice saying this License applies 137 to the Document are reproduced in all copies, and that you add no other 138 conditions whatsoever to those of this License. You may not use 139 technical measures to obstruct or control the reading or further 140 copying of the copies you make or distribute. However, you may accept 141 compensation in exchange for copies. If you distribute a large enough 142 number of copies you must also follow the conditions in section 3. 143 144 You may also lend copies, under the same conditions stated above, and 145 you may publicly display copies. 146 147 148 3. COPYING IN QUANTITY 149 150 If you publish printed copies of the Document numbering more than 100, 151 and the Document's license notice requires Cover Texts, you must enclose 152 the copies in covers that carry, clearly and legibly, all these Cover 153 Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on 154 the back cover. Both covers must also clearly and legibly identify 155 you as the publisher of these copies. The front cover must present 156 the full title with all words of the title equally prominent and 157 visible. You may add other material on the covers in addition. 158 Copying with changes limited to the covers, as long as they preserve 159 the title of the Document and satisfy these conditions, can be treated 160 as verbatim copying in other respects. 161 162 If the required texts for either cover are too voluminous to fit 163 legibly, you should put the first ones listed (as many as fit 164 reasonably) on the actual cover, and continue the rest onto adjacent 165 pages. 166 167 If you publish or distribute Opaque copies of the Document numbering 168 more than 100, you must either include a machine-readable Transparent 169 copy along with each Opaque copy, or state in or with each Opaque copy 170 a publicly-accessible computer-network location containing a complete 171 Transparent copy of the Document, free of added material, which the 172 general network-using public has access to download anonymously at no 173 charge using public-standard network protocols. If you use the latter 174 option, you must take reasonably prudent steps, when you begin 175 distribution of Opaque copies in quantity, to ensure that this 176 Transparent copy will remain thus accessible at the stated location 177 until at least one year after the last time you distribute an Opaque 178 copy (directly or through your agents or retailers) of that edition to 179 the public. 180 181 It is requested, but not required, that you contact the authors of the 182 Document well before redistributing any large number of copies, to give 183 them a chance to provide you with an updated version of the Document. 184 185 186 4. MODIFICATIONS 187 188 You may copy and distribute a Modified Version of the Document under 189 the conditions of sections 2 and 3 above, provided that you release 190 the Modified Version under precisely this License, with the Modified 191 Version filling the role of the Document, thus licensing distribution 192 and modification of the Modified Version to whoever possesses a copy 193 of it. In addition, you must do these things in the Modified Version: 194 195 A. Use in the Title Page (and on the covers, if any) a title distinct 196 from that of the Document, and from those of previous versions 197 (which should, if there were any, be listed in the History section 198 of the Document). You may use the same title as a previous version 199 if the original publisher of that version gives permission. 200 B. List on the Title Page, as authors, one or more persons or entities 201 responsible for authorship of the modifications in the Modified 202 Version, together with at least five of the principal authors of the 203 Document (all of its principal authors, if it has less than five). 204 C. State on the Title page the name of the publisher of the 205 Modified Version, as the publisher. 206 D. Preserve all the copyright notices of the Document. 207 E. Add an appropriate copyright notice for your modifications 208 adjacent to the other copyright notices. 209 F. Include, immediately after the copyright notices, a license notice 210 giving the public permission to use the Modified Version under the 211 terms of this License, in the form shown in the Addendum below. 212 G. Preserve in that license notice the full lists of Invariant Sections 213 and required Cover Texts given in the Document's license notice. 214 H. Include an unaltered copy of this License. 215 I. Preserve the section entitled "History", and its title, and add to 216 it an item stating at least the title, year, new authors, and 217 publisher of the Modified Version as given on the Title Page. If 218 there is no section entitled "History" in the Document, create one 219 stating the title, year, authors, and publisher of the Document as 220 given on its Title Page, then add an item describing the Modified 221 Version as stated in the previous sentence. 222 J. Preserve the network location, if any, given in the Document for 223 public access to a Transparent copy of the Document, and likewise 224 the network locations given in the Document for previous versions 225 it was based on. These may be placed in the "History" section. 226 You may omit a network location for a work that was published at 227 least four years before the Document itself, or if the original 228 publisher of the version it refers to gives permission. 229 K. In any section entitled "Acknowledgements" or "Dedications", 230 preserve the section's title, and preserve in the section all the 231 substance and tone of each of the contributor acknowledgements 232 and/or dedications given therein. 233 L. Preserve all the Invariant Sections of the Document, 234 unaltered in their text and in their titles. Section numbers 235 or the equivalent are not considered part of the section titles. 236 M. Delete any section entitled "Endorsements". Such a section 237 may not be included in the Modified Version. 238 N. Do not retitle any existing section as "Endorsements" 239 or to conflict in title with any Invariant Section. 240 241 If the Modified Version includes new front-matter sections or 242 appendices that qualify as Secondary Sections and contain no material 243 copied from the Document, you may at your option designate some or all 244 of these sections as invariant. To do this, add their titles to the 245 list of Invariant Sections in the Modified Version's license notice. 246 These titles must be distinct from any other section titles. 247 248 You may add a section entitled "Endorsements", provided it contains 249 nothing but endorsements of your Modified Version by various 250 parties--for example, statements of peer review or that the text has 251 been approved by an organization as the authoritative definition of a 252 standard. 253 254 You may add a passage of up to five words as a Front-Cover Text, and a 255 passage of up to 25 words as a Back-Cover Text, to the end of the list 256 of Cover Texts in the Modified Version. Only one passage of 257 Front-Cover Text and one of Back-Cover Text may be added by (or 258 through arrangements made by) any one entity. If the Document already 259 includes a cover text for the same cover, previously added by you or 260 by arrangement made by the same entity you are acting on behalf of, 261 you may not add another; but you may replace the old one, on explicit 262 permission from the previous publisher that added the old one. 263 264 The author(s) and publisher(s) of the Document do not by this License 265 give permission to use their names for publicity for or to assert or 266 imply endorsement of any Modified Version. 267 268 269 5. COMBINING DOCUMENTS 270 271 You may combine the Document with other documents released under this 272 License, under the terms defined in section 4 above for modified 273 versions, provided that you include in the combination all of the 274 Invariant Sections of all of the original documents, unmodified, and 275 list them all as Invariant Sections of your combined work in its 276 license notice. 277 278 The combined work need only contain one copy of this License, and 279 multiple identical Invariant Sections may be replaced with a single 280 copy. If there are multiple Invariant Sections with the same name but 281 different contents, make the title of each such section unique by 282 adding at the end of it, in parentheses, the name of the original 283 author or publisher of that section if known, or else a unique number. 284 Make the same adjustment to the section titles in the list of 285 Invariant Sections in the license notice of the combined work. 286 287 In the combination, you must combine any sections entitled "History" 288 in the various original documents, forming one section entitled 289 "History"; likewise combine any sections entitled "Acknowledgements", 290 and any sections entitled "Dedications". You must delete all sections 291 entitled "Endorsements." 292 293 294 6. COLLECTIONS OF DOCUMENTS 295 296 You may make a collection consisting of the Document and other documents 297 released under this License, and replace the individual copies of this 298 License in the various documents with a single copy that is included in 299 the collection, provided that you follow the rules of this License for 300 verbatim copying of each of the documents in all other respects. 301 302 You may extract a single document from such a collection, and distribute 303 it individually under this License, provided you insert a copy of this 304 License into the extracted document, and follow this License in all 305 other respects regarding verbatim copying of that document. 306 307 308 7. AGGREGATION WITH INDEPENDENT WORKS 309 310 A compilation of the Document or its derivatives with other separate 311 and independent documents or works, in or on a volume of a storage or 312 distribution medium, does not as a whole count as a Modified Version 313 of the Document, provided no compilation copyright is claimed for the 314 compilation. Such a compilation is called an "aggregate", and this 315 License does not apply to the other self-contained works thus compiled 316 with the Document, on account of their being thus compiled, if they 317 are not themselves derivative works of the Document. 318 319 If the Cover Text requirement of section 3 is applicable to these 320 copies of the Document, then if the Document is less than one quarter 321 of the entire aggregate, the Document's Cover Texts may be placed on 322 covers that surround only the Document within the aggregate. 323 Otherwise they must appear on covers around the whole aggregate. 324 325 326 8. TRANSLATION 327 328 Translation is considered a kind of modification, so you may 329 distribute translations of the Document under the terms of section 4. 330 Replacing Invariant Sections with translations requires special 331 permission from their copyright holders, but you may include 332 translations of some or all Invariant Sections in addition to the 333 original versions of these Invariant Sections. You may include a 334 translation of this License provided that you also include the 335 original English version of this License. In case of a disagreement 336 between the translation and the original English version of this 337 License, the original English version will prevail. 338 339 340 9. TERMINATION 341 342 You may not copy, modify, sublicense, or distribute the Document except 343 as expressly provided for under this License. Any other attempt to 344 copy, modify, sublicense or distribute the Document is void, and will 345 automatically terminate your rights under this License. However, 346 parties who have received copies, or rights, from you under this 347 License will not have their licenses terminated so long as such 348 parties remain in full compliance. 349 350 351 10. FUTURE REVISIONS OF THIS LICENSE 352 353 The Free Software Foundation may publish new, revised versions 354 of the GNU Free Documentation License from time to time. Such new 355 versions will be similar in spirit to the present version, but may 356 differ in detail to address new problems or concerns. See 357 http://www.gnu.org/copyleft/. 358 359 Each version of the License is given a distinguishing version number. 360 If the Document specifies that a particular numbered version of this 361 License "or any later version" applies to it, you have the option of 362 following the terms and conditions either of that specified version or 363 of any later version that has been published (not as a draft) by the 364 Free Software Foundation. If the Document does not specify a version 365 number of this License, you may choose any version ever published (not 366 as a draft) by the Free Software Foundation. 367 368 369 ADDENDUM: How to use this License for your documents 370 371 To use this License in a document you have written, include a copy of 372 the License in the document and put the following copyright and 373 license notices just after the title page: 374 375 Copyright (c) YEAR YOUR NAME. 376 Permission is granted to copy, distribute and/or modify this document 377 under the terms of the GNU Free Documentation License, Version 1.1 378 or any later version published by the Free Software Foundation; 379 with the Invariant Sections being LIST THEIR TITLES, with the 380 Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. 381 A copy of the license is included in the section entitled "GNU 382 Free Documentation License". 383 384 If you have no Invariant Sections, write "with no Invariant Sections" 385 instead of saying which ones are invariant. If you have no 386 Front-Cover Texts, write "no Front-Cover Texts" instead of 387 "Front-Cover Texts being LIST"; likewise for Back-Cover Texts. 388 389 If your document contains nontrivial examples of program code, we 390 recommend releasing these examples in parallel under your choice of 391 free software license, such as the GNU General Public License, 392 to permit their use in free software. 49 Permission is granted to copy, distribute and/or modify this document 50 under the terms of the GNU Free Documentation License, Version 1.1 or 51 any later version published by the Free Software Foundation; with no 52 Invariant Sections, with no Front-Cover Texts, and with no Back-Cover 53 Texts. A copy of the license is included in the section entitled `GNU 54 Free Documentation License''. 393 55 ============================================================================ -
lib/Makefile
re1d3f98 rb006464 13 13 14 14 # [SH] Program variables 15 objects = arc.o base64.o $(EVENT_HANDLER) ftutil.o http_client.o ini.o md5.o misc.o oauth.o oauth2.o proxy.o sha1.o $(SSL_CLIENT) url.o xmltree.o15 objects = arc.o base64.o $(EVENT_HANDLER) ftutil.o http_client.o ini.o json.o json_util.o md5.o misc.o oauth.o oauth2.o proxy.o sha1.o $(SSL_CLIENT) url.o xmltree.o 16 16 17 17 LFLAGS += -r -
lib/http_client.c
re1d3f98 rb006464 193 193 } 194 194 195 static gboolean http_handle_headers( struct http_request *req ); 196 195 197 static gboolean http_incoming_data( gpointer data, int source, b_input_condition cond ) 196 198 { 197 199 struct http_request *req = data; 198 int evil_server = 0;199 200 char buffer[2048]; 200 char * end1, *end2, *s;201 char *s; 201 202 size_t content_length; 202 203 int st; … … 218 219 packets that abort connections! \o/ */ 219 220 220 goto got_reply;221 goto eof; 221 222 } 222 223 } 223 224 else if( st == 0 ) 224 225 { 225 goto got_reply;226 goto eof; 226 227 } 227 228 } … … 239 240 else if( st == 0 ) 240 241 { 241 goto got_reply;242 } 243 } 244 245 if( st > 0 )242 goto eof; 243 } 244 } 245 246 if( st > 0 && !req->sbuf ) 246 247 { 247 248 req->reply_headers = g_realloc( req->reply_headers, req->bytes_read + st + 1 ); 248 249 memcpy( req->reply_headers + req->bytes_read, buffer, st ); 249 250 req->bytes_read += st; 250 } 251 252 st = 0; 253 } 254 255 if( st >= 0 && ( req->flags & HTTPC_STREAMING ) ) 256 { 257 if( !req->reply_body && 258 ( strstr( req->reply_headers, "\r\n\r\n" ) || 259 strstr( req->reply_headers, "\n\n" ) ) ) 260 { 261 size_t hlen; 262 263 /* We've now received all headers, so process them once 264 before we start feeding back data. */ 265 if( !http_handle_headers( req ) ) 266 return FALSE; 267 268 hlen = req->reply_body - req->reply_headers; 269 270 req->sblen = req->bytes_read - hlen; 271 req->sbuf = g_memdup( req->reply_body, req->sblen + 1 ); 272 req->reply_headers = g_realloc( req->reply_headers, hlen + 1 ); 273 274 req->reply_body = req->sbuf; 275 } 276 277 if( st > 0 ) 278 { 279 int pos = req->reply_body - req->sbuf; 280 req->sbuf = g_realloc( req->sbuf, req->sblen + st + 1 ); 281 memcpy( req->sbuf + req->sblen, buffer, st ); 282 req->bytes_read += st; 283 req->sblen += st; 284 req->sbuf[req->sblen] = '\0'; 285 req->reply_body = req->sbuf + pos; 286 req->body_size = req->sblen - pos; 287 } 288 289 if( req->reply_body ) 290 req->func( req ); 291 } 292 293 if( ssl_pending( req->ssl ) ) 294 return http_incoming_data( data, source, cond ); 251 295 252 296 /* There will be more! */ … … 255 299 http_incoming_data, req ); 256 300 257 if( ssl_pending( req->ssl ) ) 258 return http_incoming_data( data, source, cond ); 259 else 260 return FALSE; 261 262 got_reply: 301 return FALSE; 302 303 eof: 304 req->flags |= HTTPC_EOF; 305 263 306 /* Maybe if the webserver is overloaded, or when there's bad SSL 264 307 support... */ … … 269 312 } 270 313 314 if( !( req->flags & HTTPC_STREAMING ) ) 315 { 316 /* Returns FALSE if we were redirected, in which case we should abort 317 and not run any callback yet. */ 318 if( !http_handle_headers( req ) ) 319 return FALSE; 320 } 321 322 cleanup: 323 if( req->ssl ) 324 ssl_disconnect( req->ssl ); 325 else 326 closesocket( req->fd ); 327 328 if( ( s = get_rfc822_header( req->reply_headers, "Content-Length", 0 ) ) && 329 sscanf( s, "%zd", &content_length ) == 1 ) 330 { 331 if( content_length < req->body_size ) 332 { 333 req->status_code = -1; 334 g_free( req->status_string ); 335 req->status_string = g_strdup( "Response truncated" ); 336 } 337 } 338 g_free( s ); 339 340 if( getenv( "BITLBEE_DEBUG" ) && req ) 341 printf( "Finishing HTTP request with status: %s\n", 342 req->status_string ? req->status_string : "NULL" ); 343 344 req->func( req ); 345 http_free( req ); 346 return FALSE; 347 } 348 349 /* Splits headers and body. Checks result code, in case of 300s it'll handle 350 redirects. If this returns FALSE, don't call any callbacks! */ 351 static gboolean http_handle_headers( struct http_request *req ) 352 { 353 char *end1, *end2; 354 int evil_server = 0; 355 271 356 /* Zero termination is very convenient. */ 272 req->reply_headers[req->bytes_read] = 0;357 req->reply_headers[req->bytes_read] = '\0'; 273 358 274 359 /* Find the separation between headers and body, and keep stupid … … 289 374 { 290 375 req->status_string = g_strdup( "Malformed HTTP reply" ); 291 goto cleanup;376 return TRUE; 292 377 } 293 378 … … 306 391 if( ( end1 = strchr( req->reply_headers, ' ' ) ) != NULL ) 307 392 { 308 if( sscanf( end1 + 1, "% d", &req->status_code ) != 1 )393 if( sscanf( end1 + 1, "%hd", &req->status_code ) != 1 ) 309 394 { 310 395 req->status_string = g_strdup( "Can't parse status code" ); … … 349 434 { 350 435 req->status_string = g_strdup( "Can't locate Location: header" ); 351 goto cleanup;436 return TRUE; 352 437 } 353 438 … … 369 454 req->status_string = g_strdup( "Can't handle recursive redirects" ); 370 455 371 goto cleanup;456 return TRUE; 372 457 } 373 458 else … … 380 465 s = strstr( loc, "\r\n" ); 381 466 if( s == NULL ) 382 goto cleanup;467 return TRUE; 383 468 384 469 url = g_new0( url_t, 1 ); … … 389 474 req->status_string = g_strdup( "Malformed redirect URL" ); 390 475 g_free( url ); 391 goto cleanup;476 return TRUE; 392 477 } 393 478 … … 401 486 req->status_string = g_strdup( "Error while rebuilding request string" ); 402 487 g_free( url ); 403 goto cleanup;488 return TRUE; 404 489 } 405 490 … … 467 552 req->status_string = g_strdup( "Connection problem during redirect" ); 468 553 g_free( new_request ); 469 goto cleanup;554 return TRUE; 470 555 } 471 556 … … 480 565 } 481 566 482 /* Assume that a closed connection means we're finished, this indeed 483 breaks with keep-alive connections and faulty connections. */ 484 /* req->finished = 1; */ 485 486 cleanup: 567 return TRUE; 568 } 569 570 void http_flush_bytes( struct http_request *req, size_t len ) 571 { 572 if( len <= 0 || len > req->body_size || !( req->flags & HTTPC_STREAMING ) ) 573 return; 574 575 req->reply_body += len; 576 req->body_size -= len; 577 578 if( req->reply_body - req->sbuf >= 512 ) 579 { 580 printf( "Wasting %ld bytes, cleaning up stream buffer\n", req->reply_body - req->sbuf ); 581 char *new = g_memdup( req->reply_body, req->body_size + 1 ); 582 g_free( req->sbuf ); 583 req->reply_body = req->sbuf = new; 584 req->sblen = req->body_size; 585 } 586 } 587 588 void http_close( struct http_request *req ) 589 { 590 if( !req ) 591 return; 592 487 593 if( req->ssl ) 488 594 ssl_disconnect( req->ssl ); … … 490 596 closesocket( req->fd ); 491 597 492 if( ( s = get_rfc822_header( req->reply_headers, "Content-Length", 0 ) ) &&493 sscanf( s, "%zd", &content_length ) == 1 )494 {495 if( content_length < req->body_size )496 {497 req->status_code = -1;498 g_free( req->status_string );499 req->status_string = g_strdup( "Response truncated" );500 }501 }502 g_free( s );503 504 if( getenv( "BITLBEE_DEBUG" ) && req )505 printf( "Finishing HTTP request with status: %s\n",506 req->status_string ? req->status_string : "NULL" );507 508 req->func( req );509 598 http_free( req ); 510 return FALSE;511 599 } 512 600 … … 516 604 g_free( req->reply_headers ); 517 605 g_free( req->status_string ); 606 g_free( req->sbuf ); 518 607 g_free( req ); 519 608 } 520 -
lib/http_client.h
re1d3f98 rb006464 26 26 /* http_client allows you to talk (asynchronously, again) to HTTP servers. 27 27 In the "background" it will send the whole query and wait for a complete 28 response to come back. Right now it's only used by the MSN Passport 29 authentication code, but it might be useful for other things too (for 30 example the AIM usericon patch uses this so icons can be stored on 31 webservers instead of the local filesystem). 28 response to come back. Initially written for MS Passport authentication, 29 but used for many other things now like OAuth and Twitter. 32 30 33 Didn't test this too much, but it seems to work well. Just don't look 34 at the code that handles HTTP 30x redirects. ;-) The function is 35 probably not very useful for downloading lots of data since it keeps 36 everything in a memory buffer until the download is completed (and 37 can't pass any data or whatever before then). It's very useful for 38 doing quick requests without blocking the whole program, though. */ 31 It's very useful for doing quick requests without blocking the whole 32 program. Unfortunately it doesn't support fancy stuff like HTTP keep- 33 alives. */ 39 34 40 35 #include <glib.h> … … 42 37 43 38 struct http_request; 39 40 typedef enum http_client_flags 41 { 42 HTTPC_STREAMING = 1, 43 HTTPC_EOF = 2, 44 } http_client_flags_t; 44 45 45 46 /* Your callback function should look like this: */ … … 53 54 char *request; /* The request to send to the server. */ 54 55 int request_length; /* Its size. */ 55 int status_code;/* The numeric HTTP status code. (Or -156 short status_code; /* The numeric HTTP status code. (Or -1 56 57 if something really went wrong) */ 57 58 char *status_string; /* The error text. */ … … 59 60 char *reply_body; 60 61 int body_size; /* The number of bytes in reply_body. */ 61 /* int finished; Set to non-0 if the request was completed 62 successfully. */ 63 int redir_ttl; /* You can set it to 0 if you don't want 62 short redir_ttl; /* You can set it to 0 if you don't want 64 63 http_client to follow them. */ 64 65 http_client_flags_t flags; 65 66 66 67 http_input_function func; … … 68 69 69 70 /* Please don't touch the things down here, you shouldn't need them. */ 70 71 71 void *ssl; 72 72 int fd; … … 75 75 int bytes_written; 76 76 int bytes_read; 77 78 /* Used in streaming mode. Caller should read from reply_body. */ 79 char *sbuf; 80 size_t sblen; 77 81 }; 78 82 … … 83 87 struct http_request *http_dorequest( char *host, int port, int ssl, char *request, http_input_function func, gpointer data ); 84 88 struct http_request *http_dorequest_url( char *url_string, http_input_function func, gpointer data ); 89 90 /* For streaming connections only; flushes len bytes at the start of the buffer. */ 91 void http_flush_bytes( struct http_request *req, size_t len ); 92 void http_close( struct http_request *req ); -
lib/oauth2.c
re1d3f98 rb006464 4 4 * Simple OAuth client (consumer) implementation. * 5 5 * * 6 * Copyright 2010-201 1Wilmer van der Gaast <wilmer@gaast.net> *6 * Copyright 2010-2012 Wilmer van der Gaast <wilmer@gaast.net> * 7 7 * * 8 8 * This program is free software; you can redistribute it and/or modify * … … 26 26 #include "oauth2.h" 27 27 #include "oauth.h" 28 #include "json.h" 28 29 #include "url.h" 29 30 … … 44 45 }; 45 46 46 static char *oauth2_json_dumb_get( const char *json, const char *key );47 47 static void oauth2_access_token_done( struct http_request *req ); 48 48 … … 115 115 else if( content_type && strstr( content_type, "application/json" ) ) 116 116 { 117 atoken = oauth2_json_dumb_get( req->reply_body, "access_token" ); 118 rtoken = oauth2_json_dumb_get( req->reply_body, "refresh_token" ); 117 json_value *js = json_parse( req->reply_body ); 118 if( js && js->type == json_object ) 119 { 120 int i; 121 122 for( i = 0; i < js->u.object.length; i ++ ) 123 { 124 if( js->u.object.values[i].value->type != json_string ) 125 continue; 126 if( strcmp( js->u.object.values[i].name, "access_token" ) == 0 ) 127 atoken = g_strdup( js->u.object.values[i].value->u.string.ptr ); 128 if( strcmp( js->u.object.values[i].name, "refresh_token" ) == 0 ) 129 rtoken = g_strdup( js->u.object.values[i].value->u.string.ptr ); 130 } 131 } 132 json_value_free( js ); 119 133 } 120 134 else … … 136 150 g_free( cb_data ); 137 151 } 138 139 /* Super dumb. I absolutely refuse to use/add a complete json parser library140 (adding a new dependency to BitlBee for the first time in.. 6 years?) just141 to parse 100 bytes of data. So I have to do my own parsing because OAuth2142 dropped support for XML. (GRRR!) This is very dumb and for example won't143 work for integer values, nor will it strip/handle backslashes. */144 static char *oauth2_json_dumb_get( const char *json, const char *key )145 {146 int is_key = 0; /* 1 == reading key, 0 == reading value */147 int found_key = 0;148 149 while( json && *json )150 {151 /* Grab strings and see if they're what we're looking for. */152 if( *json == '"' || *json == '\'' )153 {154 char q = *json;155 const char *str_start;156 json ++;157 str_start = json;158 159 while( *json )160 {161 /* \' and \" are not string terminators. */162 if( *json == '\\' && json[1] == q )163 json ++;164 /* But without a \ it is. */165 else if( *json == q )166 break;167 json ++;168 }169 if( *json == '\0' )170 return NULL;171 172 if( is_key && strncmp( str_start, key, strlen( key ) ) == 0 )173 {174 found_key = 1;175 }176 else if( !is_key && found_key )177 {178 char *ret = g_memdup( str_start, json - str_start + 1 );179 ret[json-str_start] = '\0';180 return ret;181 }182 183 }184 else if( *json == '{' || *json == ',' )185 {186 found_key = 0;187 is_key = 1;188 }189 else if( *json == ':' )190 is_key = 0;191 192 json ++;193 }194 195 return NULL;196 } -
lib/ssl_gnutls.c
re1d3f98 rb006464 38 38 39 39 static gboolean initialized = FALSE; 40 gnutls_certificate_credentials xcred;40 gnutls_certificate_credentials_t xcred; 41 41 42 42 #include <limits.h> … … 60 60 gboolean verify; 61 61 62 gnutls_session session;62 gnutls_session_t session; 63 63 }; 64 64 … … 134 134 conn->data = data; 135 135 conn->inpa = -1; 136 conn->hostname = hostname;136 conn->hostname = g_strdup( hostname ); 137 137 138 138 /* For now, SSL verification is globally enabled by setting the cafile … … 171 171 int verifyret = 0; 172 172 gnutls_x509_crt_t cert; 173 const char *hostname;174 175 hostname= gnutls_session_get_ptr( session );173 struct scd *conn; 174 175 conn = gnutls_session_get_ptr( session ); 176 176 177 177 gnutlsret = gnutls_certificate_verify_peers2( session, &status ); … … 211 211 return VERIFY_CERT_ERROR; 212 212 213 if( !gnutls_x509_crt_check_hostname( cert, hostname ) )213 if( !gnutls_x509_crt_check_hostname( cert, conn->hostname ) ) 214 214 { 215 215 verifyret |= VERIFY_CERT_INVALID; … … 267 267 268 268 gnutls_init( &conn->session, GNUTLS_CLIENT ); 269 if( conn->verify ) 270 gnutls_session_set_ptr( conn->session, (void *) conn->hostname ); 269 gnutls_session_set_ptr( conn->session, (void *) conn ); 271 270 #if GNUTLS_VERSION_NUMBER < 0x020c00 272 271 gnutls_transport_set_lowat( conn->session, 0 ); … … 276 275 277 276 sock_make_nonblocking( conn->fd ); 278 gnutls_transport_set_ptr( conn->session, (gnutls_transport_ptr ) GNUTLS_STUPID_CAST conn->fd );277 gnutls_transport_set_ptr( conn->session, (gnutls_transport_ptr_t) GNUTLS_STUPID_CAST conn->fd ); 279 278 280 279 return ssl_handshake( data, source, cond ); … … 402 401 if( conn->session ) 403 402 gnutls_deinit( conn->session ); 403 g_free( conn->hostname ); 404 404 g_free( conn ); 405 405 } -
protocols/msn/msn.c
re1d3f98 rb006464 53 53 54 54 ic->proto_data = md; 55 ic->flags |= OPT_PONGS | OPT_PONGED; 55 56 56 57 if( strchr( acc->user, '@' ) == NULL ) -
protocols/msn/ns.c
re1d3f98 rb006464 576 576 if( num_parts >= 7 ) 577 577 handler->msglen = atoi( cmd[6] ); 578 } 579 else if( strcmp( cmd[0], "QNG" ) == 0 ) 580 { 581 ic->flags |= OPT_PONGED; 578 582 } 579 583 else if( isdigit( cmd[0][0] ) ) -
protocols/nogaim.c
re1d3f98 rb006464 261 261 struct im_connection *ic = d; 262 262 263 if( ( ic->flags & OPT_PONGS ) && !( ic->flags & OPT_PONGED ) ) 264 { 265 /* This protocol is expected to ack keepalives and hasn't 266 since the last time we were here. */ 267 imcb_error( ic, "Connection timeout" ); 268 imc_logout( ic, TRUE ); 269 return FALSE; 270 } 271 ic->flags &= ~OPT_PONGED; 272 263 273 if( ic->acc->prpl->keepalive ) 264 274 ic->acc->prpl->keepalive( ic ); 265 275 266 276 return TRUE; 277 } 278 279 void start_keepalives( struct im_connection *ic, int interval ) 280 { 281 b_event_remove( ic->keepalive ); 282 ic->keepalive = b_timeout_add( interval, send_keepalive, ic ); 283 284 /* Connecting successfully counts as a first successful pong. */ 285 if( ic->flags & OPT_PONGS ) 286 ic->flags |= OPT_PONGED; 267 287 } 268 288 … … 277 297 imcb_log( ic, "Logged in" ); 278 298 279 b_event_remove( ic->keepalive );280 ic->keepalive = b_timeout_add( 60000, send_keepalive, ic );281 299 ic->flags |= OPT_LOGGED_IN; 300 start_keepalives( ic, 60000 ); 282 301 283 302 /* Necessary to send initial presence status, even if we're not away. */ -
protocols/nogaim.h
re1d3f98 rb006464 68 68 #define OPT_THINKING 0x00000200 /* about these values... Stupid me! */ 69 69 #define OPT_NOOTR 0x00001000 /* protocol not suitable for OTR */ 70 #define OPT_PONGS 0x00010000 /* Service sends us keep-alives */ 71 #define OPT_PONGED 0x00020000 /* Received a keep-alive during last interval */ 70 72 71 73 /* ok. now the fun begins. first we create a connection structure */ -
protocols/twitter/twitter.c
re1d3f98 rb006464 5 5 * * 6 6 * Copyright 2009 Geert Mulders <g.c.w.m.mulders@gmail.com> * 7 * Copyright 2010-2012 Wilmer van der Gaast <wilmer@gaast.net> * 7 8 * * 8 9 * This library is free software; you can redistribute it and/or * … … 66 67 // Run this once. After this queue the main loop function. 67 68 twitter_main_loop(ic, -1, 0); 68 69 // Queue the main_loop 70 // Save the return value, so we can remove the timeout on logout. 71 td->main_loop_id = 72 b_timeout_add(set_getint(&ic->acc->set, "fetch_interval") * 1000, twitter_main_loop, ic); 69 70 if (set_getbool(&ic->acc->set, "stream")) { 71 /* That fetch was just to get backlog, the stream will give 72 us the rest. \o/ */ 73 twitter_open_stream(ic); 74 ic->flags |= OPT_PONGS; 75 } else { 76 /* Not using the streaming API, so keep polling the old- 77 fashioned way. :-( */ 78 td->main_loop_id = 79 b_timeout_add(set_getint(&ic->acc->set, "fetch_interval") * 1000, 80 twitter_main_loop, ic); 81 } 73 82 } 74 83 … … 279 288 280 289 s = set_add(&acc->set, "strip_newlines", "false", set_eval_bool, acc); 290 291 s = set_add(&acc->set, "stream", "true", set_eval_bool, acc); 292 s->flags |= ACC_SET_OFFLINE_ONLY; 281 293 } 282 294 … … 363 375 364 376 if (td) { 377 http_close(td->stream); 365 378 oauth_info_free(td->oauth_info); 366 379 g_free(td->user); -
protocols/twitter/twitter.h
re1d3f98 rb006464 57 57 guint64 last_status_id; /* For undo */ 58 58 gint main_loop_id; 59 struct http_request *stream; 59 60 struct groupchat *timeline_gc; 60 61 gint http_fails; -
protocols/twitter/twitter_http.c
re1d3f98 rb006464 47 47 * This is actually pretty generic function... Perhaps it should move to the lib/http_client.c 48 48 */ 49 void*twitter_http(struct im_connection *ic, char *url_string, http_input_function func,50 gpointer data, int is_post, char **arguments, int arguments_len)49 struct http_request *twitter_http(struct im_connection *ic, char *url_string, http_input_function func, 50 gpointer data, int is_post, char **arguments, int arguments_len) 51 51 { 52 52 struct twitter_data *td = ic->proto_data; … … 55 55 void *ret; 56 56 char *url_arguments; 57 url_t *base_url = NULL; 57 58 58 59 url_arguments = g_strdup(""); … … 67 68 } 68 69 } 70 71 if (strstr(url_string, "://")) { 72 base_url = g_new0(url_t, 1); 73 if (!url_set(base_url, url_string)) { 74 g_free(base_url); 75 return NULL; 76 } 77 } 78 69 79 // Make the request. 70 80 g_string_printf(request, "%s %s%s%s%s HTTP/1.0\r\n" … … 72 82 "User-Agent: BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\r\n", 73 83 is_post ? "POST" : "GET", 74 td->url_path, url_string, 75 is_post ? "" : "?", is_post ? "" : url_arguments, td->url_host); 84 base_url ? base_url->file : td->url_path, 85 base_url ? "" : url_string, 86 is_post ? "" : "?", is_post ? "" : url_arguments, 87 base_url ? base_url->host : td->url_host); 76 88 77 89 // If a pass and user are given we append them to the request. … … 80 92 char *full_url; 81 93 82 full_url = g_strconcat(set_getstr(&ic->acc->set, "base_url"), url_string, NULL); 94 if (base_url) 95 full_url = g_strdup(url_string); 96 else 97 full_url = g_strconcat(set_getstr(&ic->acc->set, "base_url"), url_string, NULL); 83 98 full_header = oauth_http_header(td->oauth_info, is_post ? "POST" : "GET", 84 99 full_url, url_arguments); … … 109 124 } 110 125 111 ret = http_dorequest(td->url_host, td->url_port, td->url_ssl, request->str, func, data); 126 if (base_url) 127 ret = http_dorequest(base_url->host, base_url->port, base_url->proto == PROTO_HTTPS, request->str, func, data); 128 else 129 ret = http_dorequest(td->url_host, td->url_port, td->url_ssl, request->str, func, data); 112 130 113 131 g_free(url_arguments); 114 132 g_string_free(request, TRUE); 133 g_free(base_url); 115 134 return ret; 116 135 } -
protocols/twitter/twitter_http.h
re1d3f98 rb006464 30 30 struct oauth_info; 31 31 32 void*twitter_http(struct im_connection *ic, char *url_string, http_input_function func,33 gpointer data, int is_post, char** arguments, int arguments_len);32 struct http_request *twitter_http(struct im_connection *ic, char *url_string, http_input_function func, 33 gpointer data, int is_post, char** arguments, int arguments_len); 34 34 35 35 #endif //_TWITTER_HTTP_H -
protocols/twitter/twitter_lib.c
re1d3f98 rb006464 35 35 #include "misc.h" 36 36 #include "base64.h" 37 #include "xmltree.h"38 37 #include "twitter_lib.h" 38 #include "json_util.h" 39 39 #include <ctype.h> 40 40 #include <errno.h> … … 168 168 { 169 169 static char *ret = NULL; 170 struct xt_node *root, *node, *err;170 json_value *root, *err; 171 171 172 172 g_free(ret); … … 174 174 175 175 if (req->body_size > 0) { 176 root = xt_from_string(req->reply_body, req->body_size);177 178 for (node = root; node; node = node->next)179 if ((err = xt_find_node(node->children, "error")) && err->text_len > 0) {180 ret = g_strdup_printf("%s (%s)", req->status_string, err->text);181 break;182 }183 184 xt_free_node(root);176 root = json_parse(req->reply_body); 177 err = json_o_get(root, "errors"); 178 if (err && err->type == json_array && (err = err->u.array.values[0]) && 179 err->type == json_object) { 180 const char *msg = json_o_str(err, "message"); 181 if (msg) 182 ret = g_strdup_printf("%s (%s)", req->status_string, msg); 183 } 184 json_value_free(root); 185 185 } 186 186 … … 188 188 } 189 189 190 static struct xt_node *twitter_parse_response(struct im_connection *ic, struct http_request *req) 190 /* WATCH OUT: This function might or might not destroy your connection. 191 Sub-optimal indeed, but just be careful when this returns NULL! */ 192 static json_value *twitter_parse_response(struct im_connection *ic, struct http_request *req) 191 193 { 192 194 gboolean logging_in = !(ic->flags & OPT_LOGGED_IN); 193 195 gboolean periodic; 194 196 struct twitter_data *td = ic->proto_data; 195 struct xt_node *ret;197 json_value *ret; 196 198 char path[64] = "", *s; 197 199 … … 211 213 throwing 401s so I'll keep treating this one as fatal 212 214 only during login. */ 213 imcb_error(ic, "Authentication failure"); 215 imcb_error(ic, "Authentication failure (%s)", 216 twitter_parse_error(req)); 214 217 imc_logout(ic, FALSE); 215 218 return NULL; … … 227 230 } 228 231 229 if ((ret = xt_from_string(req->reply_body, req->body_size)) == NULL) {232 if ((ret = json_parse(req->reply_body)) == NULL) { 230 233 imcb_error(ic, "Could not retrieve %s: %s", 231 234 path, "XML parse error"); … … 251 254 252 255 /** 253 * Function to help fill a list.254 */255 static xt_status twitter_xt_next_cursor(struct xt_node *node, struct twitter_xml_list *txl)256 {257 char *end = NULL;258 259 if (node->text)260 txl->next_cursor = g_ascii_strtoll(node->text, &end, 10);261 if (end == NULL)262 txl->next_cursor = -1;263 264 return XT_HANDLED;265 }266 267 /**268 256 * Fill a list of ids. 269 257 */ 270 static xt_status twitter_xt_get_friends_id_list(struct xt_node *node, struct twitter_xml_list *txl) 271 { 272 struct xt_node *child; 258 static gboolean twitter_xt_get_friends_id_list(json_value *node, struct twitter_xml_list *txl) 259 { 260 json_value *c; 261 int i; 273 262 274 263 // Set the list type. 275 264 txl->type = TXL_ID; 276 265 277 // The root <statuses> node should hold the list of statuses <status> 278 // Walk over the nodes children. 279 for (child = node->children; child; child = child->next) { 280 if (g_strcasecmp("ids", child->name) == 0) { 281 struct xt_node *idc; 282 for (idc = child->children; idc; idc = idc->next) 283 if (g_strcasecmp(idc->name, "id") == 0) 284 txl->list = g_slist_prepend(txl->list, 285 g_memdup(idc->text, idc->text_len + 1)); 286 } else if (g_strcasecmp("next_cursor", child->name) == 0) { 287 twitter_xt_next_cursor(child, txl); 288 } 289 } 290 291 return XT_HANDLED; 266 c = json_o_get(node, "ids"); 267 if (!c || c->type != json_array) 268 return FALSE; 269 270 for (i = 0; i < c->u.array.length; i ++) { 271 if (c->u.array.values[i]->type != json_integer) 272 continue; 273 274 txl->list = g_slist_prepend(txl->list, 275 g_strdup_printf("%lld", c->u.array.values[i]->u.integer)); 276 277 } 278 279 c = json_o_get(node, "next_cursor"); 280 if (c && c->type == json_integer) 281 txl->next_cursor = c->u.integer; 282 else 283 txl->next_cursor = -1; 284 285 return TRUE; 292 286 } 293 287 … … 300 294 { 301 295 struct im_connection *ic; 302 struct xt_node *parsed;296 json_value *parsed; 303 297 struct twitter_xml_list *txl; 304 298 struct twitter_data *td; … … 322 316 if (!(parsed = twitter_parse_response(ic, req))) 323 317 return; 318 324 319 twitter_xt_get_friends_id_list(parsed, txl); 325 xt_free_node(parsed);320 json_value_free(parsed); 326 321 327 322 td->follow_ids = txl->list; … … 338 333 } 339 334 340 static xt_status twitter_xt_get_users(struct xt_node *node, struct twitter_xml_list *txl);335 static gboolean twitter_xt_get_users(json_value *node, struct twitter_xml_list *txl); 341 336 static void twitter_http_get_users_lookup(struct http_request *req); 342 337 … … 379 374 { 380 375 struct im_connection *ic = req->data; 381 struct xt_node *parsed;376 json_value *parsed; 382 377 struct twitter_xml_list *txl; 383 378 GSList *l = NULL; … … 395 390 return; 396 391 twitter_xt_get_users(parsed, txl); 397 xt_free_node(parsed);392 json_value_free(parsed); 398 393 399 394 // Add the users as buddies. … … 409 404 } 410 405 411 /** 412 * Function to fill a twitter_xml_user struct. 413 * It sets: 414 * - the name and 415 * - the screen_name. 416 */ 417 static xt_status twitter_xt_get_user(struct xt_node *node, struct twitter_xml_user *txu) 418 { 419 struct xt_node *child; 420 421 // Walk over the nodes children. 422 for (child = node->children; child; child = child->next) { 423 if (g_strcasecmp("name", child->name) == 0) { 424 txu->name = g_memdup(child->text, child->text_len + 1); 425 } else if (g_strcasecmp("screen_name", child->name) == 0) { 426 txu->screen_name = g_memdup(child->text, child->text_len + 1); 427 } 428 } 429 return XT_HANDLED; 406 struct twitter_xml_user *twitter_xt_get_user(const json_value *node) 407 { 408 struct twitter_xml_user *txu; 409 410 txu = g_new0(struct twitter_xml_user, 1); 411 txu->name = g_strdup(json_o_str(node, "name")); 412 txu->screen_name = g_strdup(json_o_str(node, "screen_name")); 413 414 return txu; 430 415 } 431 416 … … 435 420 * - all <user>s from the <users> element. 436 421 */ 437 static xt_status twitter_xt_get_users(struct xt_node *node, struct twitter_xml_list *txl)422 static gboolean twitter_xt_get_users(json_value *node, struct twitter_xml_list *txl) 438 423 { 439 424 struct twitter_xml_user *txu; 440 struct xt_node *child;425 int i; 441 426 442 427 // Set the type of the list. 443 428 txl->type = TXL_USER; 444 429 430 if (!node || node->type != json_array) 431 return FALSE; 432 445 433 // The root <users> node should hold the list of users <user> 446 434 // Walk over the nodes children. 447 for (child = node->children; child; child = child->next) { 448 if (g_strcasecmp("user", child->name) == 0) { 449 txu = g_new0(struct twitter_xml_user, 1); 450 twitter_xt_get_user(child, txu); 451 // Put the item in the front of the list. 435 for (i = 0; i < node->u.array.length; i ++) { 436 txu = twitter_xt_get_user(node->u.array.values[i]); 437 if (txu) 452 438 txl->list = g_slist_prepend(txl->list, txu); 453 } 454 } 455 456 return XT_HANDLED; 439 } 440 441 return TRUE; 457 442 } 458 443 … … 462 447 #define TWITTER_TIME_FORMAT "%a %b %d %H:%M:%S +0000 %Y" 463 448 #endif 449 450 static char* expand_entities(char* text, const json_value *entities); 464 451 465 452 /** … … 471 458 * - the user in a twitter_xml_user struct. 472 459 */ 473 static xt_status twitter_xt_get_status(struct xt_node *node, struct twitter_xml_status *txs) 474 { 475 struct xt_node *child, *rt = NULL; 476 477 // Walk over the nodes children. 478 for (child = node->children; child; child = child->next) { 479 if (g_strcasecmp("text", child->name) == 0) { 480 txs->text = g_memdup(child->text, child->text_len + 1); 481 } else if (g_strcasecmp("retweeted_status", child->name) == 0) { 482 rt = child; 483 } else if (g_strcasecmp("created_at", child->name) == 0) { 460 static gboolean twitter_xt_get_status(const json_value *node, struct twitter_xml_status *txs) 461 { 462 const json_value *rt = NULL, *entities = NULL; 463 464 if (node->type != json_object) 465 return FALSE; 466 467 JSON_O_FOREACH (node, k, v) { 468 if (strcmp("text", k) == 0 && v->type == json_string) { 469 txs->text = g_memdup(v->u.string.ptr, v->u.string.length + 1); 470 } else if (strcmp("retweeted_status", k) == 0 && v->type == json_object) { 471 rt = v; 472 } else if (strcmp("created_at", k) == 0 && v->type == json_string) { 484 473 struct tm parsed; 485 474 … … 487 476 this field. :-( Also assumes the timezone used 488 477 is UTC since C time handling functions suck. */ 489 if (strptime( child->text, TWITTER_TIME_FORMAT, &parsed) != NULL)478 if (strptime(v->u.string.ptr, TWITTER_TIME_FORMAT, &parsed) != NULL) 490 479 txs->created_at = mktime_utc(&parsed); 491 } else if (g_strcasecmp("user", child->name) == 0) { 492 txs->user = g_new0(struct twitter_xml_user, 1); 493 twitter_xt_get_user(child, txs->user); 494 } else if (g_strcasecmp("id", child->name) == 0) { 495 txs->id = g_ascii_strtoull(child->text, NULL, 10); 496 } else if (g_strcasecmp("in_reply_to_status_id", child->name) == 0) { 497 txs->reply_to = g_ascii_strtoull(child->text, NULL, 10); 480 } else if (strcmp("user", k) == 0 && v->type == json_object) { 481 txs->user = twitter_xt_get_user(v); 482 } else if (strcmp("id", k) == 0 && v->type == json_integer) { 483 txs->id = v->u.integer; 484 } else if (strcmp("in_reply_to_status_id", k) == 0 && v->type == json_integer) { 485 txs->reply_to = v->u.integer; 486 } else if (strcmp("entities", k) == 0 && v->type == json_object) { 487 entities = v; 498 488 } 499 489 } … … 503 493 if (rt) { 504 494 struct twitter_xml_status *rtxs = g_new0(struct twitter_xml_status, 1); 505 if ( twitter_xt_get_status(rt, rtxs) != XT_HANDLED) {495 if (!twitter_xt_get_status(rt, rtxs)) { 506 496 txs_free(rtxs); 507 return XT_HANDLED;497 return TRUE; 508 498 } 509 499 … … 511 501 txs->text = g_strdup_printf("RT @%s: %s", rtxs->user->screen_name, rtxs->text); 512 502 txs_free(rtxs); 513 } else { 514 struct xt_node *urls, *url; 503 } else if (entities) { 504 txs->text = expand_entities(txs->text, entities); 505 } 506 507 return txs->text && txs->user && txs->id; 508 } 509 510 /** 511 * Function to fill a twitter_xml_status struct (DM variant). 512 */ 513 static gboolean twitter_xt_get_dm(const json_value *node, struct twitter_xml_status *txs) 514 { 515 const json_value *entities = NULL; 516 517 if (node->type != json_object) 518 return FALSE; 519 520 JSON_O_FOREACH (node, k, v) { 521 if (strcmp("text", k) == 0 && v->type == json_string) { 522 txs->text = g_memdup(v->u.string.ptr, v->u.string.length + 1); 523 } else if (strcmp("created_at", k) == 0 && v->type == json_string) { 524 struct tm parsed; 525 526 /* Very sensitive to changes to the formatting of 527 this field. :-( Also assumes the timezone used 528 is UTC since C time handling functions suck. */ 529 if (strptime(v->u.string.ptr, TWITTER_TIME_FORMAT, &parsed) != NULL) 530 txs->created_at = mktime_utc(&parsed); 531 } else if (strcmp("sender", k) == 0 && v->type == json_object) { 532 txs->user = twitter_xt_get_user(v); 533 } else if (strcmp("id", k) == 0 && v->type == json_integer) { 534 txs->id = v->u.integer; 535 } 536 } 537 538 if (entities) { 539 txs->text = expand_entities(txs->text, entities); 540 } 541 542 return txs->text && txs->user && txs->id; 543 } 544 545 static char* expand_entities(char* text, const json_value *entities) { 546 JSON_O_FOREACH (entities, k, v) { 547 int i; 515 548 516 urls = xt_find_path(node, "entities"); 517 if (urls != NULL) 518 urls = urls->children; 519 for (; urls; urls = urls->next) { 520 if (strcmp(urls->name, "urls") != 0 && strcmp(urls->name, "media") != 0) 549 if (v->type != json_array) 550 continue; 551 if (strcmp(k, "urls") != 0 && strcmp(k, "media") != 0) 552 continue; 553 554 for (i = 0; i < v->u.array.length; i ++) { 555 if (v->u.array.values[i]->type != json_object) 521 556 continue; 522 557 523 for (url = urls ? urls->children : NULL; url; url = url->next) { 524 /* "short" is a reserved word. :-P */ 525 struct xt_node *kort = xt_find_node(url->children, "url"); 526 struct xt_node *disp = xt_find_node(url->children, "display_url"); 527 char *pos, *new; 528 529 if (!kort || !kort->text || !disp || !disp->text || 530 !(pos = strstr(txs->text, kort->text))) 531 continue; 532 533 *pos = '\0'; 534 new = g_strdup_printf("%s%s <%s>%s", txs->text, kort->text, 535 disp->text, pos + strlen(kort->text)); 536 537 g_free(txs->text); 538 txs->text = new; 539 } 558 const char *kort = json_o_str(v->u.array.values[i], "url"); 559 const char *disp = json_o_str(v->u.array.values[i], "display_url"); 560 char *pos, *new; 561 562 if (!kort || !disp || !(pos = strstr(text, kort))) 563 continue; 564 565 *pos = '\0'; 566 new = g_strdup_printf("%s%s <%s>%s", text, kort, 567 disp, pos + strlen(kort)); 568 569 g_free(text); 570 text = new; 540 571 } 541 572 } 542 543 return XT_HANDLED;573 574 return text; 544 575 } 545 576 … … 550 581 * - the next_cursor. 551 582 */ 552 static xt_status twitter_xt_get_status_list(struct im_connection *ic, struct xt_node *node,553 583 static gboolean twitter_xt_get_status_list(struct im_connection *ic, const json_value *node, 584 struct twitter_xml_list *txl) 554 585 { 555 586 struct twitter_xml_status *txs; 556 struct xt_node *child;557 587 bee_user_t *bu; 588 int i; 558 589 559 590 // Set the type of the list. 560 591 txl->type = TXL_STATUS; 592 593 if (node->type != json_array) 594 return FALSE; 561 595 562 596 // The root <statuses> node should hold the list of statuses <status> 563 597 // Walk over the nodes children. 564 for (child = node->children; child; child = child->next) { 565 if (g_strcasecmp("status", child->name) == 0) { 566 txs = g_new0(struct twitter_xml_status, 1); 567 twitter_xt_get_status(child, txs); 568 // Put the item in the front of the list. 569 txl->list = g_slist_prepend(txl->list, txs); 570 571 if (txs->user && txs->user->screen_name && 572 (bu = bee_user_by_handle(ic->bee, ic, txs->user->screen_name))) { 573 struct twitter_user_data *tud = bu->data; 574 575 if (txs->id > tud->last_id) { 576 tud->last_id = txs->id; 577 tud->last_time = txs->created_at; 578 } 598 for (i = 0; i < node->u.array.length; i ++) { 599 txs = g_new0(struct twitter_xml_status, 1); 600 twitter_xt_get_status(node->u.array.values[i], txs); 601 // Put the item in the front of the list. 602 txl->list = g_slist_prepend(txl->list, txs); 603 604 if (txs->user && txs->user->screen_name && 605 (bu = bee_user_by_handle(ic->bee, ic, txs->user->screen_name))) { 606 struct twitter_user_data *tud = bu->data; 607 608 if (txs->id > tud->last_id) { 609 tud->last_id = txs->id; 610 tud->last_time = txs->created_at; 579 611 } 580 } else if (g_strcasecmp("next_cursor", child->name) == 0) {581 twitter_xt_next_cursor(child, txl);582 612 } 583 613 } 584 614 585 return XT_HANDLED;615 return TRUE; 586 616 } 587 617 … … 740 770 } 741 771 742 static void twitter_http_get_home_timeline(struct http_request *req); 743 static void twitter_http_get_mentions(struct http_request *req); 772 static gboolean twitter_stream_handle_object(struct im_connection *ic, json_value *o); 773 774 static void twitter_http_stream(struct http_request *req) 775 { 776 struct im_connection *ic = req->data; 777 struct twitter_data *td; 778 json_value *parsed; 779 int len = 0; 780 char c, *nl; 781 782 if (!g_slist_find(twitter_connections, ic)) 783 return; 784 785 ic->flags |= OPT_PONGED; 786 td = ic->proto_data; 787 788 if ((req->flags & HTTPC_EOF) || !req->reply_body) { 789 td->stream = NULL; 790 imcb_error(ic, "Stream closed (%s)", req->status_string); 791 imc_logout(ic, TRUE); 792 return; 793 } 794 795 printf( "%d bytes in stream\n", req->body_size ); 796 797 /* MUST search for CRLF, not just LF: 798 https://dev.twitter.com/docs/streaming-apis/processing#Parsing_responses */ 799 nl = strstr(req->reply_body, "\r\n"); 800 801 if (!nl) { 802 printf("Incomplete data\n"); 803 return; 804 } 805 806 len = nl - req->reply_body; 807 if (len > 0) { 808 c = req->reply_body[len]; 809 req->reply_body[len] = '\0'; 810 811 printf("JSON: %s\n", req->reply_body); 812 printf("parsed: %p\n", (parsed = json_parse(req->reply_body))); 813 if (parsed) { 814 twitter_stream_handle_object(ic, parsed); 815 } 816 json_value_free(parsed); 817 req->reply_body[len] = c; 818 } 819 820 http_flush_bytes(req, len + 2); 821 822 /* One notification might bring multiple events! */ 823 if (req->body_size > 0) 824 twitter_http_stream(req); 825 } 826 827 static gboolean twitter_stream_handle_object(struct im_connection *ic, json_value *o) 828 { 829 struct twitter_xml_status *txs = g_new0(struct twitter_xml_status, 1); 830 json_value *c; 831 832 if (twitter_xt_get_status(o, txs)) { 833 GSList *output = g_slist_append(NULL, txs); 834 twitter_groupchat(ic, output); 835 txs_free(txs); 836 g_slist_free(output); 837 return TRUE; 838 } else if ((c = json_o_get(o, "direct_message")) && 839 twitter_xt_get_dm(c, txs)) { 840 GSList *output = g_slist_append(NULL, txs); 841 twitter_private_message_chat(ic, output); 842 txs_free(txs); 843 g_slist_free(output); 844 return TRUE; 845 } 846 txs_free(txs); 847 return FALSE; 848 } 849 850 gboolean twitter_open_stream(struct im_connection *ic) 851 { 852 struct twitter_data *td = ic->proto_data; 853 char *args[2] = {"with", "followings"}; 854 855 if ((td->stream = twitter_http(ic, TWITTER_USER_STREAM_URL, 856 twitter_http_stream, ic, 0, args, 2))) { 857 /* This flag must be enabled or we'll get no data until EOF 858 (which err, kind of, defeats the purpose of a streaming API). */ 859 td->stream->flags |= HTTPC_STREAMING; 860 return TRUE; 861 } 862 863 return FALSE; 864 } 865 866 static void twitter_get_home_timeline(struct im_connection *ic, gint64 next_cursor); 867 static void twitter_get_mentions(struct im_connection *ic, gint64 next_cursor); 744 868 745 869 /** … … 823 947 } 824 948 949 static void twitter_http_get_home_timeline(struct http_request *req); 950 static void twitter_http_get_mentions(struct http_request *req); 951 825 952 /** 826 953 * Get the timeline. 827 954 */ 828 void twitter_get_home_timeline(struct im_connection *ic, gint64 next_cursor)955 static void twitter_get_home_timeline(struct im_connection *ic, gint64 next_cursor) 829 956 { 830 957 struct twitter_data *td = ic->proto_data; … … 862 989 * Get mentions. 863 990 */ 864 void twitter_get_mentions(struct im_connection *ic, gint64 next_cursor)991 static void twitter_get_mentions(struct im_connection *ic, gint64 next_cursor) 865 992 { 866 993 struct twitter_data *td = ic->proto_data; … … 893 1020 894 1021 g_free(args[1]); 895 if (td->timeline_id) { 896 g_free(args[5]); 897 } 1022 g_free(args[5]); 898 1023 } 899 1024 … … 905 1030 struct im_connection *ic = req->data; 906 1031 struct twitter_data *td; 907 struct xt_node *parsed;1032 json_value *parsed; 908 1033 struct twitter_xml_list *txl; 909 1034 … … 921 1046 goto end; 922 1047 twitter_xt_get_status_list(ic, parsed, txl); 923 xt_free_node(parsed);1048 json_value_free(parsed); 924 1049 925 1050 td->home_timeline_obj = txl; 926 1051 927 1052 end: 1053 if (!g_slist_find(twitter_connections, ic)) 1054 return; 1055 928 1056 td->flags |= TWITTER_GOT_TIMELINE; 929 1057 … … 938 1066 struct im_connection *ic = req->data; 939 1067 struct twitter_data *td; 940 struct xt_node *parsed;1068 json_value *parsed; 941 1069 struct twitter_xml_list *txl; 942 1070 … … 954 1082 goto end; 955 1083 twitter_xt_get_status_list(ic, parsed, txl); 956 xt_free_node(parsed);1084 json_value_free(parsed); 957 1085 958 1086 td->mentions_obj = txl; 959 1087 960 1088 end: 1089 if (!g_slist_find(twitter_connections, ic)) 1090 return; 1091 961 1092 td->flags |= TWITTER_GOT_MENTIONS; 962 1093 … … 972 1103 struct im_connection *ic = req->data; 973 1104 struct twitter_data *td; 974 struct xt_node *parsed, *node;1105 json_value *parsed, *id; 975 1106 976 1107 // Check if the connection is still active. … … 984 1115 return; 985 1116 986 if ((node = xt_find_node(parsed, "status")) && 987 (node = xt_find_node(node->children, "id")) && node->text) 988 td->last_status_id = g_ascii_strtoull(node->text, NULL, 10); 1117 if ((id = json_o_get(parsed, "id")) && id->type == json_integer) 1118 td->last_status_id = id->u.integer; 989 1119 } 990 1120 … … 1032 1162 char *url; 1033 1163 url = g_strdup_printf("%s%llu%s", TWITTER_STATUS_DESTROY_URL, 1034 (unsigned long long) id, ". xml");1164 (unsigned long long) id, ".json"); 1035 1165 twitter_http(ic, url, twitter_http_post, ic, 1, NULL, 0); 1036 1166 g_free(url); … … 1041 1171 char *url; 1042 1172 url = g_strdup_printf("%s%llu%s", TWITTER_STATUS_RETWEET_URL, 1043 (unsigned long long) id, ". xml");1173 (unsigned long long) id, ".json"); 1044 1174 twitter_http(ic, url, twitter_http_post, ic, 1, NULL, 0); 1045 1175 g_free(url); … … 1067 1197 char *url; 1068 1198 url = g_strdup_printf("%s%llu%s", TWITTER_FAVORITE_CREATE_URL, 1069 (unsigned long long) id, ". xml");1199 (unsigned long long) id, ".json"); 1070 1200 twitter_http(ic, url, twitter_http_post, ic, 1, NULL, 0); 1071 1201 g_free(url); -
protocols/twitter/twitter_lib.h
re1d3f98 rb006464 29 29 #include "twitter_http.h" 30 30 31 #define TWITTER_API_URL "http://api.twitter.com/1 "31 #define TWITTER_API_URL "http://api.twitter.com/1.1" 32 32 #define IDENTICA_API_URL "https://identi.ca/api" 33 33 34 34 /* Status URLs */ 35 #define TWITTER_STATUS_UPDATE_URL "/statuses/update. xml"35 #define TWITTER_STATUS_UPDATE_URL "/statuses/update.json" 36 36 #define TWITTER_STATUS_SHOW_URL "/statuses/show/" 37 37 #define TWITTER_STATUS_DESTROY_URL "/statuses/destroy/" … … 39 39 40 40 /* Timeline URLs */ 41 #define TWITTER_PUBLIC_TIMELINE_URL "/statuses/public_timeline. xml"42 #define TWITTER_FEATURED_USERS_URL "/statuses/featured. xml"43 #define TWITTER_FRIENDS_TIMELINE_URL "/statuses/friends_timeline. xml"44 #define TWITTER_HOME_TIMELINE_URL "/statuses/home_timeline. xml"45 #define TWITTER_MENTIONS_URL "/statuses/mentions .xml"46 #define TWITTER_USER_TIMELINE_URL "/statuses/user_timeline. xml"41 #define TWITTER_PUBLIC_TIMELINE_URL "/statuses/public_timeline.json" 42 #define TWITTER_FEATURED_USERS_URL "/statuses/featured.json" 43 #define TWITTER_FRIENDS_TIMELINE_URL "/statuses/friends_timeline.json" 44 #define TWITTER_HOME_TIMELINE_URL "/statuses/home_timeline.json" 45 #define TWITTER_MENTIONS_URL "/statuses/mentions_timeline.json" 46 #define TWITTER_USER_TIMELINE_URL "/statuses/user_timeline.json" 47 47 48 48 /* Users URLs */ 49 #define TWITTER_USERS_LOOKUP_URL "/users/lookup. xml"49 #define TWITTER_USERS_LOOKUP_URL "/users/lookup.json" 50 50 51 51 /* Direct messages URLs */ 52 #define TWITTER_DIRECT_MESSAGES_URL "/direct_messages. xml"53 #define TWITTER_DIRECT_MESSAGES_NEW_URL "/direct_messages/new. xml"54 #define TWITTER_DIRECT_MESSAGES_SENT_URL "/direct_messages/sent. xml"52 #define TWITTER_DIRECT_MESSAGES_URL "/direct_messages.json" 53 #define TWITTER_DIRECT_MESSAGES_NEW_URL "/direct_messages/new.json" 54 #define TWITTER_DIRECT_MESSAGES_SENT_URL "/direct_messages/sent.json" 55 55 #define TWITTER_DIRECT_MESSAGES_DESTROY_URL "/direct_messages/destroy/" 56 56 57 57 /* Friendships URLs */ 58 #define TWITTER_FRIENDSHIPS_CREATE_URL "/friendships/create. xml"59 #define TWITTER_FRIENDSHIPS_DESTROY_URL "/friendships/destroy. xml"60 #define TWITTER_FRIENDSHIPS_SHOW_URL "/friendships/show. xml"58 #define TWITTER_FRIENDSHIPS_CREATE_URL "/friendships/create.json" 59 #define TWITTER_FRIENDSHIPS_DESTROY_URL "/friendships/destroy.json" 60 #define TWITTER_FRIENDSHIPS_SHOW_URL "/friendships/show.json" 61 61 62 62 /* Social graphs URLs */ 63 #define TWITTER_FRIENDS_IDS_URL "/friends/ids. xml"64 #define TWITTER_FOLLOWERS_IDS_URL "/followers/ids. xml"63 #define TWITTER_FRIENDS_IDS_URL "/friends/ids.json" 64 #define TWITTER_FOLLOWERS_IDS_URL "/followers/ids.json" 65 65 66 66 /* Account URLs */ 67 #define TWITTER_ACCOUNT_RATE_LIMIT_URL "/account/rate_limit_status. xml"67 #define TWITTER_ACCOUNT_RATE_LIMIT_URL "/account/rate_limit_status.json" 68 68 69 69 /* Favorites URLs */ 70 #define TWITTER_FAVORITES_GET_URL "/favorites. xml"70 #define TWITTER_FAVORITES_GET_URL "/favorites.json" 71 71 #define TWITTER_FAVORITE_CREATE_URL "/favorites/create/" 72 72 #define TWITTER_FAVORITE_DESTROY_URL "/favorites/destroy/" … … 77 77 78 78 /* Report spam */ 79 #define TWITTER_REPORT_SPAM_URL "/report_spam. xml"79 #define TWITTER_REPORT_SPAM_URL "/report_spam.json" 80 80 81 #define TWITTER_USER_STREAM_URL "https://userstream.twitter.com/1.1/user.json" 82 83 gboolean twitter_open_stream(struct im_connection *ic); 81 84 void twitter_get_timeline(struct im_connection *ic, gint64 next_cursor); 82 85 void twitter_get_friends_ids(struct im_connection *ic, gint64 next_cursor); 83 void twitter_get_home_timeline(struct im_connection *ic, gint64 next_cursor);84 void twitter_get_mentions(struct im_connection *ic, gint64 next_cursor);85 86 void twitter_get_statuses_friends(struct im_connection *ic, gint64 next_cursor); 86 87
Note: See TracChangeset
for help on using the changeset viewer.