Changes in / [b006464:e1d3f98]
- Files:
-
- 4 deleted
- 16 edited
Legend:
- Unmodified
- Added
- Removed
-
debian/copyright
rb006464 re1d3f98 8 8 9 9 Mainly Copyright 2002-2012 Wilmer van der Gaast. 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 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. 21 12 22 13 BitlBee License: … … 42 33 43 34 The SGML-formatted documentation is written by Jelmer Vernooij 44 <jelmer@ samba.org> under the GNU Free Documentation License:35 <jelmer@nl.linux.org> under the GNU Free Documentation License: 45 36 46 37 ============================================================================ 47 Copyright (c) 2002-2012 Jelmer Vernooij, Wilmer van der Gaast. 48 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''. 38 GNU Free Documentation License 39 Version 1.1, March 2000 40 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. 55 393 ============================================================================ -
lib/Makefile
rb006464 re1d3f98 13 13 14 14 # [SH] Program variables 15 objects = arc.o base64.o $(EVENT_HANDLER) ftutil.o http_client.o ini.o json.o json_util.omd5.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 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
rb006464 re1d3f98 193 193 } 194 194 195 static gboolean http_handle_headers( struct http_request *req );196 197 195 static gboolean http_incoming_data( gpointer data, int source, b_input_condition cond ) 198 196 { 199 197 struct http_request *req = data; 198 int evil_server = 0; 200 199 char buffer[2048]; 201 char * s;200 char *end1, *end2, *s; 202 201 size_t content_length; 203 202 int st; … … 219 218 packets that abort connections! \o/ */ 220 219 221 goto eof;220 goto got_reply; 222 221 } 223 222 } 224 223 else if( st == 0 ) 225 224 { 226 goto eof;225 goto got_reply; 227 226 } 228 227 } … … 240 239 else if( st == 0 ) 241 240 { 242 goto eof;243 } 244 } 245 246 if( st > 0 && !req->sbuf)241 goto got_reply; 242 } 243 } 244 245 if( st > 0 ) 247 246 { 248 247 req->reply_headers = g_realloc( req->reply_headers, req->bytes_read + st + 1 ); 249 248 memcpy( req->reply_headers + req->bytes_read, buffer, st ); 250 249 req->bytes_read += st; 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 ); 250 } 295 251 296 252 /* There will be more! */ … … 299 255 http_incoming_data, req ); 300 256 301 return FALSE; 302 303 eof: 304 req->flags |= HTTPC_EOF; 305 257 if( ssl_pending( req->ssl ) ) 258 return http_incoming_data( data, source, cond ); 259 else 260 return FALSE; 261 262 got_reply: 306 263 /* Maybe if the webserver is overloaded, or when there's bad SSL 307 264 support... */ … … 312 269 } 313 270 314 if( !( req->flags & HTTPC_STREAMING ) )315 {316 /* Returns FALSE if we were redirected, in which case we should abort317 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 else326 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 handle350 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 356 271 /* Zero termination is very convenient. */ 357 req->reply_headers[req->bytes_read] = '\0';272 req->reply_headers[req->bytes_read] = 0; 358 273 359 274 /* Find the separation between headers and body, and keep stupid … … 374 289 { 375 290 req->status_string = g_strdup( "Malformed HTTP reply" ); 376 return TRUE;291 goto cleanup; 377 292 } 378 293 … … 391 306 if( ( end1 = strchr( req->reply_headers, ' ' ) ) != NULL ) 392 307 { 393 if( sscanf( end1 + 1, "% hd", &req->status_code ) != 1 )308 if( sscanf( end1 + 1, "%d", &req->status_code ) != 1 ) 394 309 { 395 310 req->status_string = g_strdup( "Can't parse status code" ); … … 434 349 { 435 350 req->status_string = g_strdup( "Can't locate Location: header" ); 436 return TRUE;351 goto cleanup; 437 352 } 438 353 … … 454 369 req->status_string = g_strdup( "Can't handle recursive redirects" ); 455 370 456 return TRUE;371 goto cleanup; 457 372 } 458 373 else … … 465 380 s = strstr( loc, "\r\n" ); 466 381 if( s == NULL ) 467 return TRUE;382 goto cleanup; 468 383 469 384 url = g_new0( url_t, 1 ); … … 474 389 req->status_string = g_strdup( "Malformed redirect URL" ); 475 390 g_free( url ); 476 return TRUE;391 goto cleanup; 477 392 } 478 393 … … 486 401 req->status_string = g_strdup( "Error while rebuilding request string" ); 487 402 g_free( url ); 488 return TRUE;403 goto cleanup; 489 404 } 490 405 … … 552 467 req->status_string = g_strdup( "Connection problem during redirect" ); 553 468 g_free( new_request ); 554 return TRUE;469 goto cleanup; 555 470 } 556 471 … … 565 480 } 566 481 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 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: 593 487 if( req->ssl ) 594 488 ssl_disconnect( req->ssl ); … … 596 490 closesocket( req->fd ); 597 491 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 ); 598 509 http_free( req ); 510 return FALSE; 599 511 } 600 512 … … 604 516 g_free( req->reply_headers ); 605 517 g_free( req->status_string ); 606 g_free( req->sbuf );607 518 g_free( req ); 608 519 } 520 -
lib/http_client.h
rb006464 re1d3f98 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. Initially written for MS Passport authentication, 29 but used for many other things now like OAuth and Twitter. 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). 30 32 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. */ 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. */ 34 39 35 40 #include <glib.h> … … 37 42 38 43 struct http_request; 39 40 typedef enum http_client_flags41 {42 HTTPC_STREAMING = 1,43 HTTPC_EOF = 2,44 } http_client_flags_t;45 44 46 45 /* Your callback function should look like this: */ … … 54 53 char *request; /* The request to send to the server. */ 55 54 int request_length; /* Its size. */ 56 short status_code;/* The numeric HTTP status code. (Or -155 int status_code; /* The numeric HTTP status code. (Or -1 57 56 if something really went wrong) */ 58 57 char *status_string; /* The error text. */ … … 60 59 char *reply_body; 61 60 int body_size; /* The number of bytes in reply_body. */ 62 short redir_ttl; /* You can set it to 0 if you don't want 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 63 64 http_client to follow them. */ 64 65 http_client_flags_t flags;66 65 67 66 http_input_function func; … … 69 68 70 69 /* 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;81 77 }; 82 78 … … 87 83 struct http_request *http_dorequest( char *host, int port, int ssl, char *request, http_input_function func, gpointer data ); 88 84 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
rb006464 re1d3f98 4 4 * Simple OAuth client (consumer) implementation. * 5 5 * * 6 * Copyright 2010-201 2Wilmer van der Gaast <wilmer@gaast.net> *6 * Copyright 2010-2011 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"29 28 #include "url.h" 30 29 … … 45 44 }; 46 45 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 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 ); 117 atoken = oauth2_json_dumb_get( req->reply_body, "access_token" ); 118 rtoken = oauth2_json_dumb_get( req->reply_body, "refresh_token" ); 133 119 } 134 120 else … … 150 136 g_free( cb_data ); 151 137 } 138 139 /* Super dumb. I absolutely refuse to use/add a complete json parser library 140 (adding a new dependency to BitlBee for the first time in.. 6 years?) just 141 to parse 100 bytes of data. So I have to do my own parsing because OAuth2 142 dropped support for XML. (GRRR!) This is very dumb and for example won't 143 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
rb006464 re1d3f98 38 38 39 39 static gboolean initialized = FALSE; 40 gnutls_certificate_credentials _txcred;40 gnutls_certificate_credentials xcred; 41 41 42 42 #include <limits.h> … … 60 60 gboolean verify; 61 61 62 gnutls_session _tsession;62 gnutls_session session; 63 63 }; 64 64 … … 134 134 conn->data = data; 135 135 conn->inpa = -1; 136 conn->hostname = g_strdup( hostname );136 conn->hostname = 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 struct scd *conn;174 175 conn= gnutls_session_get_ptr( session );173 const char *hostname; 174 175 hostname = 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, conn->hostname ) )213 if( !gnutls_x509_crt_check_hostname( cert, hostname ) ) 214 214 { 215 215 verifyret |= VERIFY_CERT_INVALID; … … 267 267 268 268 gnutls_init( &conn->session, GNUTLS_CLIENT ); 269 gnutls_session_set_ptr( conn->session, (void *) conn ); 269 if( conn->verify ) 270 gnutls_session_set_ptr( conn->session, (void *) conn->hostname ); 270 271 #if GNUTLS_VERSION_NUMBER < 0x020c00 271 272 gnutls_transport_set_lowat( conn->session, 0 ); … … 275 276 276 277 sock_make_nonblocking( conn->fd ); 277 gnutls_transport_set_ptr( conn->session, (gnutls_transport_ptr _t) GNUTLS_STUPID_CAST conn->fd );278 gnutls_transport_set_ptr( conn->session, (gnutls_transport_ptr) GNUTLS_STUPID_CAST conn->fd ); 278 279 279 280 return ssl_handshake( data, source, cond ); … … 401 402 if( conn->session ) 402 403 gnutls_deinit( conn->session ); 403 g_free( conn->hostname );404 404 g_free( conn ); 405 405 } -
protocols/msn/msn.c
rb006464 re1d3f98 53 53 54 54 ic->proto_data = md; 55 ic->flags |= OPT_PONGS | OPT_PONGED;56 55 57 56 if( strchr( acc->user, '@' ) == NULL ) -
protocols/msn/ns.c
rb006464 re1d3f98 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;582 578 } 583 579 else if( isdigit( cmd[0][0] ) ) -
protocols/nogaim.c
rb006464 re1d3f98 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't266 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 273 263 if( ic->acc->prpl->keepalive ) 274 264 ic->acc->prpl->keepalive( ic ); 275 265 276 266 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;287 267 } 288 268 … … 297 277 imcb_log( ic, "Logged in" ); 298 278 279 b_event_remove( ic->keepalive ); 280 ic->keepalive = b_timeout_add( 60000, send_keepalive, ic ); 299 281 ic->flags |= OPT_LOGGED_IN; 300 start_keepalives( ic, 60000 );301 282 302 283 /* Necessary to send initial presence status, even if we're not away. */ -
protocols/nogaim.h
rb006464 re1d3f98 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 */72 70 73 71 /* ok. now the fun begins. first we create a connection structure */ -
protocols/twitter/twitter.c
rb006464 re1d3f98 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> *8 7 * * 9 8 * This library is free software; you can redistribute it and/or * … … 67 66 // Run this once. After this queue the main loop function. 68 67 twitter_main_loop(ic, -1, 0); 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 } 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); 82 73 } 83 74 … … 288 279 289 280 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;293 281 } 294 282 … … 375 363 376 364 if (td) { 377 http_close(td->stream);378 365 oauth_info_free(td->oauth_info); 379 366 g_free(td->user); -
protocols/twitter/twitter.h
rb006464 re1d3f98 57 57 guint64 last_status_id; /* For undo */ 58 58 gint main_loop_id; 59 struct http_request *stream;60 59 struct groupchat *timeline_gc; 61 60 gint http_fails; -
protocols/twitter/twitter_http.c
rb006464 re1d3f98 47 47 * This is actually pretty generic function... Perhaps it should move to the lib/http_client.c 48 48 */ 49 struct http_request*twitter_http(struct im_connection *ic, char *url_string, http_input_function func,50 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) 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;58 57 59 58 url_arguments = g_strdup(""); … … 68 67 } 69 68 } 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 79 69 // Make the request. 80 70 g_string_printf(request, "%s %s%s%s%s HTTP/1.0\r\n" … … 82 72 "User-Agent: BitlBee " BITLBEE_VERSION " " ARCH "/" CPU "\r\n", 83 73 is_post ? "POST" : "GET", 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); 74 td->url_path, url_string, 75 is_post ? "" : "?", is_post ? "" : url_arguments, td->url_host); 88 76 89 77 // If a pass and user are given we append them to the request. … … 92 80 char *full_url; 93 81 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); 82 full_url = g_strconcat(set_getstr(&ic->acc->set, "base_url"), url_string, NULL); 98 83 full_header = oauth_http_header(td->oauth_info, is_post ? "POST" : "GET", 99 84 full_url, url_arguments); … … 124 109 } 125 110 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); 111 ret = http_dorequest(td->url_host, td->url_port, td->url_ssl, request->str, func, data); 130 112 131 113 g_free(url_arguments); 132 114 g_string_free(request, TRUE); 133 g_free(base_url);134 115 return ret; 135 116 } -
protocols/twitter/twitter_http.h
rb006464 re1d3f98 30 30 struct oauth_info; 31 31 32 struct http_request*twitter_http(struct im_connection *ic, char *url_string, http_input_function func,33 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); 34 34 35 35 #endif //_TWITTER_HTTP_H -
protocols/twitter/twitter_lib.c
rb006464 re1d3f98 35 35 #include "misc.h" 36 36 #include "base64.h" 37 #include "xmltree.h" 37 38 #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 json_value *root, *err;170 struct xt_node *root, *node, *err; 171 171 172 172 g_free(ret); … … 174 174 175 175 if (req->body_size > 0) { 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);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); 185 185 } 186 186 … … 188 188 } 189 189 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) 190 static struct xt_node *twitter_parse_response(struct im_connection *ic, struct http_request *req) 193 191 { 194 192 gboolean logging_in = !(ic->flags & OPT_LOGGED_IN); 195 193 gboolean periodic; 196 194 struct twitter_data *td = ic->proto_data; 197 json_value *ret;195 struct xt_node *ret; 198 196 char path[64] = "", *s; 199 197 … … 213 211 throwing 401s so I'll keep treating this one as fatal 214 212 only during login. */ 215 imcb_error(ic, "Authentication failure (%s)", 216 twitter_parse_error(req)); 213 imcb_error(ic, "Authentication failure"); 217 214 imc_logout(ic, FALSE); 218 215 return NULL; … … 230 227 } 231 228 232 if ((ret = json_parse(req->reply_body)) == NULL) {229 if ((ret = xt_from_string(req->reply_body, req->body_size)) == NULL) { 233 230 imcb_error(ic, "Could not retrieve %s: %s", 234 231 path, "XML parse error"); … … 254 251 255 252 /** 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 /** 256 268 * Fill a list of ids. 257 269 */ 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; 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; 262 273 263 274 // Set the list type. 264 275 txl->type = TXL_ID; 265 276 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; 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; 286 292 } 287 293 … … 294 300 { 295 301 struct im_connection *ic; 296 json_value *parsed;302 struct xt_node *parsed; 297 303 struct twitter_xml_list *txl; 298 304 struct twitter_data *td; … … 316 322 if (!(parsed = twitter_parse_response(ic, req))) 317 323 return; 318 319 324 twitter_xt_get_friends_id_list(parsed, txl); 320 json_value_free(parsed);325 xt_free_node(parsed); 321 326 322 327 td->follow_ids = txl->list; … … 333 338 } 334 339 335 static gboolean twitter_xt_get_users(json_value *node, struct twitter_xml_list *txl);340 static xt_status twitter_xt_get_users(struct xt_node *node, struct twitter_xml_list *txl); 336 341 static void twitter_http_get_users_lookup(struct http_request *req); 337 342 … … 374 379 { 375 380 struct im_connection *ic = req->data; 376 json_value *parsed;381 struct xt_node *parsed; 377 382 struct twitter_xml_list *txl; 378 383 GSList *l = NULL; … … 390 395 return; 391 396 twitter_xt_get_users(parsed, txl); 392 json_value_free(parsed);397 xt_free_node(parsed); 393 398 394 399 // Add the users as buddies. … … 404 409 } 405 410 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; 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; 415 430 } 416 431 … … 420 435 * - all <user>s from the <users> element. 421 436 */ 422 static gboolean twitter_xt_get_users(json_value *node, struct twitter_xml_list *txl)437 static xt_status twitter_xt_get_users(struct xt_node *node, struct twitter_xml_list *txl) 423 438 { 424 439 struct twitter_xml_user *txu; 425 int i;440 struct xt_node *child; 426 441 427 442 // Set the type of the list. 428 443 txl->type = TXL_USER; 429 444 430 if (!node || node->type != json_array)431 return FALSE;432 433 445 // The root <users> node should hold the list of users <user> 434 446 // Walk over the nodes children. 435 for (i = 0; i < node->u.array.length; i ++) { 436 txu = twitter_xt_get_user(node->u.array.values[i]); 437 if (txu) 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. 438 452 txl->list = g_slist_prepend(txl->list, txu); 439 } 440 441 return TRUE; 453 } 454 } 455 456 return XT_HANDLED; 442 457 } 443 458 … … 447 462 #define TWITTER_TIME_FORMAT "%a %b %d %H:%M:%S +0000 %Y" 448 463 #endif 449 450 static char* expand_entities(char* text, const json_value *entities);451 464 452 465 /** … … 458 471 * - the user in a twitter_xml_user struct. 459 472 */ 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) { 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) { 473 484 struct tm parsed; 474 485 … … 476 487 this field. :-( Also assumes the timezone used 477 488 is UTC since C time handling functions suck. */ 478 if (strptime( v->u.string.ptr, TWITTER_TIME_FORMAT, &parsed) != NULL)489 if (strptime(child->text, TWITTER_TIME_FORMAT, &parsed) != NULL) 479 490 txs->created_at = mktime_utc(&parsed); 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; 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); 488 498 } 489 499 } … … 493 503 if (rt) { 494 504 struct twitter_xml_status *rtxs = g_new0(struct twitter_xml_status, 1); 495 if ( !twitter_xt_get_status(rt, rtxs)) {505 if (twitter_xt_get_status(rt, rtxs) != XT_HANDLED) { 496 506 txs_free(rtxs); 497 return TRUE;507 return XT_HANDLED; 498 508 } 499 509 … … 501 511 txs->text = g_strdup_printf("RT @%s: %s", rtxs->user->screen_name, rtxs->text); 502 512 txs_free(rtxs); 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; 513 } else { 514 struct xt_node *urls, *url; 548 515 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) 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) 556 521 continue; 557 522 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; 571 } 572 } 573 574 return text; 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 } 540 } 541 } 542 543 return XT_HANDLED; 575 544 } 576 545 … … 581 550 * - the next_cursor. 582 551 */ 583 static gboolean twitter_xt_get_status_list(struct im_connection *ic, const json_value *node,584 552 static xt_status twitter_xt_get_status_list(struct im_connection *ic, struct xt_node *node, 553 struct twitter_xml_list *txl) 585 554 { 586 555 struct twitter_xml_status *txs; 556 struct xt_node *child; 587 557 bee_user_t *bu; 588 int i;589 558 590 559 // Set the type of the list. 591 560 txl->type = TXL_STATUS; 592 593 if (node->type != json_array)594 return FALSE;595 561 596 562 // The root <statuses> node should hold the list of statuses <status> 597 563 // Walk over the nodes children. 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; 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 } 611 579 } 612 } 613 } 614 615 return TRUE; 580 } else if (g_strcasecmp("next_cursor", child->name) == 0) { 581 twitter_xt_next_cursor(child, txl); 582 } 583 } 584 585 return XT_HANDLED; 616 586 } 617 587 … … 770 740 } 771 741 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); 742 static void twitter_http_get_home_timeline(struct http_request *req); 743 static void twitter_http_get_mentions(struct http_request *req); 868 744 869 745 /** … … 947 823 } 948 824 949 static void twitter_http_get_home_timeline(struct http_request *req);950 static void twitter_http_get_mentions(struct http_request *req);951 952 825 /** 953 826 * Get the timeline. 954 827 */ 955 staticvoid twitter_get_home_timeline(struct im_connection *ic, gint64 next_cursor)828 void twitter_get_home_timeline(struct im_connection *ic, gint64 next_cursor) 956 829 { 957 830 struct twitter_data *td = ic->proto_data; … … 989 862 * Get mentions. 990 863 */ 991 staticvoid twitter_get_mentions(struct im_connection *ic, gint64 next_cursor)864 void twitter_get_mentions(struct im_connection *ic, gint64 next_cursor) 992 865 { 993 866 struct twitter_data *td = ic->proto_data; … … 1020 893 1021 894 g_free(args[1]); 1022 g_free(args[5]); 895 if (td->timeline_id) { 896 g_free(args[5]); 897 } 1023 898 } 1024 899 … … 1030 905 struct im_connection *ic = req->data; 1031 906 struct twitter_data *td; 1032 json_value *parsed;907 struct xt_node *parsed; 1033 908 struct twitter_xml_list *txl; 1034 909 … … 1046 921 goto end; 1047 922 twitter_xt_get_status_list(ic, parsed, txl); 1048 json_value_free(parsed);923 xt_free_node(parsed); 1049 924 1050 925 td->home_timeline_obj = txl; 1051 926 1052 927 end: 1053 if (!g_slist_find(twitter_connections, ic))1054 return;1055 1056 928 td->flags |= TWITTER_GOT_TIMELINE; 1057 929 … … 1066 938 struct im_connection *ic = req->data; 1067 939 struct twitter_data *td; 1068 json_value *parsed;940 struct xt_node *parsed; 1069 941 struct twitter_xml_list *txl; 1070 942 … … 1082 954 goto end; 1083 955 twitter_xt_get_status_list(ic, parsed, txl); 1084 json_value_free(parsed);956 xt_free_node(parsed); 1085 957 1086 958 td->mentions_obj = txl; 1087 959 1088 960 end: 1089 if (!g_slist_find(twitter_connections, ic))1090 return;1091 1092 961 td->flags |= TWITTER_GOT_MENTIONS; 1093 962 … … 1103 972 struct im_connection *ic = req->data; 1104 973 struct twitter_data *td; 1105 json_value *parsed, *id;974 struct xt_node *parsed, *node; 1106 975 1107 976 // Check if the connection is still active. … … 1115 984 return; 1116 985 1117 if ((id = json_o_get(parsed, "id")) && id->type == json_integer) 1118 td->last_status_id = id->u.integer; 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); 1119 989 } 1120 990 … … 1162 1032 char *url; 1163 1033 url = g_strdup_printf("%s%llu%s", TWITTER_STATUS_DESTROY_URL, 1164 (unsigned long long) id, ". json");1034 (unsigned long long) id, ".xml"); 1165 1035 twitter_http(ic, url, twitter_http_post, ic, 1, NULL, 0); 1166 1036 g_free(url); … … 1171 1041 char *url; 1172 1042 url = g_strdup_printf("%s%llu%s", TWITTER_STATUS_RETWEET_URL, 1173 (unsigned long long) id, ". json");1043 (unsigned long long) id, ".xml"); 1174 1044 twitter_http(ic, url, twitter_http_post, ic, 1, NULL, 0); 1175 1045 g_free(url); … … 1197 1067 char *url; 1198 1068 url = g_strdup_printf("%s%llu%s", TWITTER_FAVORITE_CREATE_URL, 1199 (unsigned long long) id, ". json");1069 (unsigned long long) id, ".xml"); 1200 1070 twitter_http(ic, url, twitter_http_post, ic, 1, NULL, 0); 1201 1071 g_free(url); -
protocols/twitter/twitter_lib.h
rb006464 re1d3f98 29 29 #include "twitter_http.h" 30 30 31 #define TWITTER_API_URL "http://api.twitter.com/1 .1"31 #define TWITTER_API_URL "http://api.twitter.com/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. json"35 #define TWITTER_STATUS_UPDATE_URL "/statuses/update.xml" 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. 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"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" 47 47 48 48 /* Users URLs */ 49 #define TWITTER_USERS_LOOKUP_URL "/users/lookup. json"49 #define TWITTER_USERS_LOOKUP_URL "/users/lookup.xml" 50 50 51 51 /* Direct messages URLs */ 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"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" 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. json"59 #define TWITTER_FRIENDSHIPS_DESTROY_URL "/friendships/destroy. json"60 #define TWITTER_FRIENDSHIPS_SHOW_URL "/friendships/show. json"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" 61 61 62 62 /* Social graphs URLs */ 63 #define TWITTER_FRIENDS_IDS_URL "/friends/ids. json"64 #define TWITTER_FOLLOWERS_IDS_URL "/followers/ids. json"63 #define TWITTER_FRIENDS_IDS_URL "/friends/ids.xml" 64 #define TWITTER_FOLLOWERS_IDS_URL "/followers/ids.xml" 65 65 66 66 /* Account URLs */ 67 #define TWITTER_ACCOUNT_RATE_LIMIT_URL "/account/rate_limit_status. json"67 #define TWITTER_ACCOUNT_RATE_LIMIT_URL "/account/rate_limit_status.xml" 68 68 69 69 /* Favorites URLs */ 70 #define TWITTER_FAVORITES_GET_URL "/favorites. json"70 #define TWITTER_FAVORITES_GET_URL "/favorites.xml" 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. json"79 #define TWITTER_REPORT_SPAM_URL "/report_spam.xml" 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);84 81 void twitter_get_timeline(struct im_connection *ic, gint64 next_cursor); 85 82 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); 86 85 void twitter_get_statuses_friends(struct im_connection *ic, gint64 next_cursor); 87 86
Note: See TracChangeset
for help on using the changeset viewer.