gwenhywfar
4.3.1
|
00001 /*************************************************************************** 00002 begin : Wed Apr 28 2010 00003 copyright : (C) 2010 by Martin Preuss 00004 email : martin@libchipcard.de 00005 00006 *************************************************************************** 00007 * * 00008 * This library is free software; you can redistribute it and/or * 00009 * modify it under the terms of the GNU Lesser General Public * 00010 * License as published by the Free Software Foundation; either * 00011 * version 2.1 of the License, or (at your option) any later version. * 00012 * * 00013 * This library is distributed in the hope that it will be useful, * 00014 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * 00016 * Lesser General Public License for more details. * 00017 * * 00018 * You should have received a copy of the GNU Lesser General Public * 00019 * License along with this library; if not, write to the Free Software * 00020 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * 00021 * MA 02111-1307 USA * 00022 * * 00023 ***************************************************************************/ 00024 00025 #ifdef HAVE_CONFIG_H 00026 # include <config.h> 00027 #endif 00028 00029 #define DISABLE_DEBUGLOG 00030 00031 00032 00033 #include "syncio_http_p.h" 00034 #include "i18n_l.h" 00035 00036 #include <gwenhywfar/misc.h> 00037 #include <gwenhywfar/debug.h> 00038 #include <gwenhywfar/gui.h> 00039 #include <gwenhywfar/text.h> 00040 00041 #include <assert.h> 00042 #include <errno.h> 00043 #include <string.h> 00044 #include <ctype.h> 00045 00046 00047 00048 GWEN_INHERIT(GWEN_SYNCIO, GWEN_SYNCIO_HTTP) 00049 00050 00051 00052 GWEN_SYNCIO *GWEN_SyncIo_Http_new(GWEN_SYNCIO *baseIo) { 00053 GWEN_SYNCIO *sio; 00054 GWEN_SYNCIO_HTTP *xio; 00055 00056 sio=GWEN_SyncIo_new(GWEN_SYNCIO_HTTP_TYPE, baseIo); 00057 GWEN_NEW_OBJECT(GWEN_SYNCIO_HTTP, xio); 00058 GWEN_INHERIT_SETDATA(GWEN_SYNCIO, GWEN_SYNCIO_HTTP, sio, xio, GWEN_SyncIo_Http_FreeData); 00059 00060 GWEN_SyncIo_SetConnectFn(sio, GWEN_SyncIo_Http_Connect); 00061 GWEN_SyncIo_SetDisconnectFn(sio, GWEN_SyncIo_Http_Disconnect); 00062 GWEN_SyncIo_SetReadFn(sio, GWEN_SyncIo_Http_Read); 00063 GWEN_SyncIo_SetWriteFn(sio, GWEN_SyncIo_Http_Write); 00064 00065 xio->dbCommandIn=GWEN_DB_Group_new("command"); 00066 xio->dbStatusIn=GWEN_DB_Group_new("status"); 00067 xio->dbHeaderIn=GWEN_DB_Group_new("header"); 00068 00069 xio->dbCommandOut=GWEN_DB_Group_new("command"); 00070 xio->dbStatusOut=GWEN_DB_Group_new("status"); 00071 xio->dbHeaderOut=GWEN_DB_Group_new("header"); 00072 00073 00074 return sio; 00075 } 00076 00077 00078 00079 void GWENHYWFAR_CB GWEN_SyncIo_Http_FreeData(void *bp, void *p) { 00080 GWEN_SYNCIO_HTTP *xio; 00081 00082 xio=(GWEN_SYNCIO_HTTP*) p; 00083 00084 GWEN_DB_Group_free(xio->dbCommandOut); 00085 GWEN_DB_Group_free(xio->dbStatusOut); 00086 GWEN_DB_Group_free(xio->dbHeaderOut); 00087 00088 GWEN_DB_Group_free(xio->dbCommandIn); 00089 GWEN_DB_Group_free(xio->dbStatusIn); 00090 GWEN_DB_Group_free(xio->dbHeaderIn); 00091 00092 GWEN_FREE_OBJECT(xio); 00093 } 00094 00095 00096 00097 int GWENHYWFAR_CB GWEN_SyncIo_Http_Connect(GWEN_SYNCIO *sio) { 00098 GWEN_SYNCIO_HTTP *xio; 00099 GWEN_SYNCIO *baseIo; 00100 int rv; 00101 00102 assert(sio); 00103 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_HTTP, sio); 00104 assert(xio); 00105 00106 if (GWEN_SyncIo_GetStatus(sio)==GWEN_SyncIo_Status_Connected) { 00107 DBG_INFO(GWEN_LOGDOMAIN, "Already connected"); 00108 return 0; 00109 } 00110 00111 baseIo=GWEN_SyncIo_GetBaseIo(sio); 00112 assert(baseIo); 00113 00114 rv=GWEN_SyncIo_Connect(baseIo); 00115 if (rv<0) { 00116 DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv); 00117 return rv; 00118 } 00119 00120 GWEN_SyncIo_SetStatus(sio, GWEN_SyncIo_Status_Connected); 00121 GWEN_SyncIo_Http_SetReadIdle(sio); 00122 00123 return 0; 00124 } 00125 00126 00127 00128 int GWENHYWFAR_CB GWEN_SyncIo_Http_Disconnect(GWEN_SYNCIO *sio) { 00129 GWEN_SYNCIO_HTTP *xio; 00130 GWEN_SYNCIO *baseIo; 00131 int rv; 00132 00133 assert(sio); 00134 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_HTTP, sio); 00135 assert(xio); 00136 00137 if (GWEN_SyncIo_GetStatus(sio)!=GWEN_SyncIo_Status_Connected) { 00138 DBG_INFO(GWEN_LOGDOMAIN, "Not connected"); 00139 return GWEN_ERROR_NOT_CONNECTED; 00140 } 00141 00142 baseIo=GWEN_SyncIo_GetBaseIo(sio); 00143 assert(baseIo); 00144 00145 rv=GWEN_SyncIo_Disconnect(baseIo); 00146 GWEN_SyncIo_SetStatus(sio, GWEN_SyncIo_Status_Disconnected); 00147 if (rv<0) { 00148 DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv); 00149 return rv; 00150 } 00151 00152 return 0; 00153 } 00154 00155 00156 00157 void GWEN_SyncIo_Http_SetReadIdle(GWEN_SYNCIO *sio) { 00158 GWEN_SYNCIO_HTTP *xio; 00159 00160 assert(sio); 00161 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_HTTP, sio); 00162 assert(xio); 00163 00164 xio->readMode=GWEN_SyncIo_Http_Mode_Idle; 00165 } 00166 00167 00168 00169 int GWENHYWFAR_CB GWEN_SyncIo_Http_Read(GWEN_SYNCIO *sio, 00170 uint8_t *buffer, 00171 uint32_t size) { 00172 GWEN_SYNCIO_HTTP *xio; 00173 int rv; 00174 00175 assert(sio); 00176 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_HTTP, sio); 00177 assert(xio); 00178 00179 if (GWEN_SyncIo_GetStatus(sio)!=GWEN_SyncIo_Status_Connected) { 00180 DBG_ERROR(GWEN_LOGDOMAIN, "Not connected"); 00181 return GWEN_ERROR_NOT_CONNECTED; 00182 } 00183 00184 if (xio->readMode==GWEN_SyncIo_Http_Mode_Idle) { 00185 const char *s; 00186 00187 /* reset status and headers */ 00188 GWEN_DB_ClearGroup(xio->dbCommandIn, NULL); 00189 GWEN_DB_ClearGroup(xio->dbStatusIn, NULL); 00190 GWEN_DB_ClearGroup(xio->dbHeaderIn, NULL); 00191 00192 if (GWEN_SyncIo_GetFlags(sio) & GWEN_SYNCIO_FLAGS_PASSIVE) { 00193 /* read command */ 00194 rv=GWEN_SyncIo_Http_ReadCommand(sio); 00195 if (rv<0) { 00196 DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv); 00197 xio->readMode=GWEN_SyncIo_Http_Mode_Error; 00198 return rv; 00199 } 00200 00201 /* possibly read header */ 00202 s=GWEN_DB_GetCharValue(xio->dbCommandIn, "protocol", 0, "HTTP/1.0"); 00203 if (!(s && strcasecmp(s, "HTTP/0.9")==0)) { 00204 rv=GWEN_SyncIo_Http_ReadHeader(sio); 00205 if (rv<0) { 00206 DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv); 00207 xio->readMode=GWEN_SyncIo_Http_Mode_Error; 00208 return rv; 00209 } 00210 } 00211 } 00212 else { 00213 /* read status */ 00214 rv=GWEN_SyncIo_Http_ReadStatus(sio); 00215 if (rv<0) { 00216 DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv); 00217 xio->readMode=GWEN_SyncIo_Http_Mode_Error; 00218 return rv; 00219 } 00220 00221 /* possibly read header */ 00222 s=GWEN_DB_GetCharValue(xio->dbStatusIn, "protocol", 0, "HTTP/1.0"); 00223 if (!(s && strcasecmp(s, "HTTP/0.9")==0)) { 00224 rv=GWEN_SyncIo_Http_ReadHeader(sio); 00225 if (rv<0) { 00226 DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv); 00227 xio->readMode=GWEN_SyncIo_Http_Mode_Error; 00228 return rv; 00229 } 00230 } 00231 } 00232 00233 } 00234 00235 if (xio->readMode==GWEN_SyncIo_Http_Mode_ChunkSize) { 00236 rv=GWEN_SyncIo_Http_ReadChunkSize(sio); 00237 if (rv<0) { 00238 DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv); 00239 xio->readMode=GWEN_SyncIo_Http_Mode_Error; 00240 return rv; 00241 } 00242 if (xio->currentReadChunkSize==0) { 00243 int rv2; 00244 GWEN_BUFFER *tbuf; 00245 00246 /* all chunks finished, read trailing CR/LF */ 00247 tbuf=GWEN_Buffer_new(0, 256, 0, 1); 00248 rv2=GWEN_SyncIo_Http_ReadLine(sio, tbuf); 00249 if (rv2<0) { 00250 DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv2); 00251 GWEN_Buffer_free(tbuf); 00252 return rv2; 00253 } 00254 GWEN_Buffer_free(tbuf); 00255 00256 DBG_DEBUG(GWEN_LOGDOMAIN, "Chunks finished."); 00257 00258 /* chunksize is 0, body ended */ 00259 GWEN_SyncIo_Http_SetReadIdle(sio); 00260 return 0; 00261 } 00262 else if (xio->currentReadChunkSize==-1) { 00263 DBG_ERROR(GWEN_LOGDOMAIN, "Undetermined chunksize in chunked mode? Aborting."); 00264 xio->readMode=GWEN_SyncIo_Http_Mode_Error; 00265 return GWEN_ERROR_BAD_DATA; 00266 } 00267 00268 /* chunksize known, next will be to read that chunk */ 00269 xio->readMode=GWEN_SyncIo_Http_Mode_Chunk; 00270 } 00271 00272 if (xio->readMode==GWEN_SyncIo_Http_Mode_Chunk) { 00273 /* read chunk */ 00274 rv=GWEN_SyncIo_Http_ReadChunk(sio, buffer, size); 00275 if (rv<0) { 00276 xio->readMode=GWEN_SyncIo_Http_Mode_Error; 00277 DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv); 00278 return rv; 00279 } 00280 00281 return rv; 00282 } 00283 00284 if (xio->readMode==GWEN_SyncIo_Http_Mode_Body) { 00285 /* read chunk */ 00286 rv=GWEN_SyncIo_Http_ReadBody(sio, buffer, size); 00287 if (rv<0) { 00288 xio->readMode=GWEN_SyncIo_Http_Mode_Error; 00289 DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv); 00290 return rv; 00291 } 00292 00293 return rv; 00294 } 00295 00296 if (xio->readMode==GWEN_SyncIo_Http_Mode_Error) { 00297 DBG_ERROR(GWEN_LOGDOMAIN, "Previous read error"); 00298 return GWEN_ERROR_GENERIC; 00299 } 00300 00301 return 0; 00302 } 00303 00304 00305 00306 int GWENHYWFAR_CB GWEN_SyncIo_Http_Write(GWEN_SYNCIO *sio, 00307 const uint8_t *buffer, 00308 uint32_t size) { 00309 GWEN_SYNCIO_HTTP *xio; 00310 GWEN_SYNCIO *baseIo; 00311 int rv; 00312 00313 assert(sio); 00314 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_HTTP, sio); 00315 assert(xio); 00316 00317 baseIo=GWEN_SyncIo_GetBaseIo(sio); 00318 assert(baseIo); 00319 00320 if (GWEN_SyncIo_GetStatus(sio)!=GWEN_SyncIo_Status_Connected) { 00321 DBG_ERROR(GWEN_LOGDOMAIN, "Not connected"); 00322 return GWEN_ERROR_NOT_CONNECTED; 00323 } 00324 00325 if (xio->writeMode==GWEN_SyncIo_Http_Mode_Idle) { 00326 const char *s; 00327 00328 if (GWEN_SyncIo_GetFlags(sio) & GWEN_SYNCIO_FLAGS_PASSIVE) 00329 /* write status */ 00330 rv=GWEN_SyncIo_Http_WriteStatus(sio); 00331 else 00332 /* write command */ 00333 rv=GWEN_SyncIo_Http_WriteCommand(sio); 00334 if (rv<0) { 00335 xio->writeMode=GWEN_SyncIo_Http_Mode_Error; 00336 return rv; 00337 } 00338 00339 /* possibly write header */ 00340 s=GWEN_DB_GetCharValue(xio->dbCommandOut, "protocol", 0, "HTTP/1.0"); 00341 if (!(s && strcasecmp(s, "HTTP/0.9")==0)) { 00342 rv=GWEN_SyncIo_Http_WriteHeader(sio); 00343 if (rv<0) { 00344 xio->writeMode=GWEN_SyncIo_Http_Mode_Error; 00345 return rv; 00346 } 00347 } 00348 } 00349 00350 if (xio->writeMode==GWEN_SyncIo_Http_Mode_ChunkSize) { 00351 rv=GWEN_SyncIo_Http_WriteChunkSize(sio, size); 00352 if (rv<0) { 00353 xio->writeMode=GWEN_SyncIo_Http_Mode_Error; 00354 return rv; 00355 } 00356 if (size==0) { 00357 /* chunksize is 0, body ended */ 00358 GWEN_SyncIo_Http_SetWriteIdle(sio); 00359 return 0; 00360 } 00361 00362 /* chunksize known, next will be to write that chunk */ 00363 xio->writeMode=GWEN_SyncIo_Http_Mode_Chunk; 00364 } 00365 00366 if (xio->writeMode==GWEN_SyncIo_Http_Mode_Chunk) { 00367 /* we want to write binary data transparently */ 00368 GWEN_SyncIo_AddFlags(baseIo, GWEN_SYNCIO_FLAGS_TRANSPARENT); 00369 rv=GWEN_SyncIo_WriteForced(baseIo, buffer, size); 00370 if (rv<0) { 00371 xio->writeMode=GWEN_SyncIo_Http_Mode_Error; 00372 return rv; 00373 } 00374 xio->writeMode=GWEN_SyncIo_Http_Mode_ChunkSize; 00375 00376 return rv; 00377 } 00378 00379 if (xio->writeMode==GWEN_SyncIo_Http_Mode_Body) { 00380 if ((xio->currentWriteBodySize!=-1) && 00381 (size>xio->currentWriteBodySize)) { 00382 DBG_ERROR(GWEN_LOGDOMAIN, "Size is beyond total body size (%d)!", size); 00383 xio->writeMode=GWEN_SyncIo_Http_Mode_Error; 00384 return GWEN_ERROR_INVALID; 00385 } 00386 00387 /* we want to write binary data transparently */ 00388 GWEN_SyncIo_AddFlags(baseIo, GWEN_SYNCIO_FLAGS_TRANSPARENT); 00389 rv=GWEN_SyncIo_WriteForced(baseIo, buffer, size); 00390 if (rv<0) { 00391 xio->writeMode=GWEN_SyncIo_Http_Mode_Error; 00392 return rv; 00393 } 00394 if (xio->currentWriteBodySize!=-1) { 00395 xio->currentWriteBodySize-=rv; 00396 if (xio->currentWriteBodySize==0) 00397 GWEN_SyncIo_Http_SetWriteIdle(sio); 00398 } 00399 00400 return rv; 00401 } 00402 00403 if (xio->writeMode==GWEN_SyncIo_Http_Mode_Error) { 00404 DBG_ERROR(GWEN_LOGDOMAIN, "Previous write error"); 00405 return GWEN_ERROR_GENERIC; 00406 } 00407 00408 return 0; 00409 } 00410 00411 00412 00413 int GWEN_SyncIo_Http_ReadLine(GWEN_SYNCIO *sio, GWEN_BUFFER *tbuf) { 00414 GWEN_SYNCIO_HTTP *xio; 00415 GWEN_SYNCIO *baseIo; 00416 int rv; 00417 00418 assert(sio); 00419 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_HTTP, sio); 00420 assert(xio); 00421 00422 baseIo=GWEN_SyncIo_GetBaseIo(sio); 00423 assert(baseIo); 00424 00425 /* we want to read a text line, so we can't have a transparent mode in the base layer */ 00426 GWEN_SyncIo_SubFlags(baseIo, GWEN_SYNCIO_FLAGS_TRANSPARENT); 00427 00428 /* read a single line */ 00429 do { 00430 uint8_t *p; 00431 uint32_t l; 00432 00433 GWEN_Buffer_AllocRoom(tbuf, 1024); 00434 p=(uint8_t*) GWEN_Buffer_GetPosPointer(tbuf); 00435 l=GWEN_Buffer_GetMaxUnsegmentedWrite(tbuf); 00436 rv=GWEN_SyncIo_Read(baseIo, p, l); 00437 if (rv<0) { 00438 DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv); 00439 return rv; 00440 } 00441 else if (rv>0) { 00442 GWEN_Buffer_IncrementPos(tbuf, rv); 00443 GWEN_Buffer_AdjustUsedBytes(tbuf); 00444 if (p[rv-1]==10) { 00445 p[rv-1]=0; 00446 break; 00447 } 00448 } 00449 else if (rv==0) 00450 break; 00451 } while(rv>0); 00452 00453 if (GWEN_Buffer_GetUsedBytes(tbuf)<1) { 00454 DBG_ERROR(GWEN_LOGDOMAIN, "Nothing received"); 00455 return GWEN_ERROR_EOF; 00456 } 00457 00458 return 0; 00459 } 00460 00461 00462 00463 int GWEN_SyncIo_Http_ParseStatus(GWEN_SYNCIO *sio, char *buffer) { 00464 GWEN_SYNCIO_HTTP *xio; 00465 char *p; 00466 char *s; 00467 int code; 00468 00469 assert(sio); 00470 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_HTTP, sio); 00471 assert(xio); 00472 00473 s=buffer; 00474 00475 /* read protocol */ 00476 p=strchr(s, ' '); 00477 if (!p) { 00478 DBG_ERROR(GWEN_LOGDOMAIN, 00479 "Bad format of HTTP status (%s)", buffer); 00480 return GWEN_ERROR_INVALID; 00481 } 00482 *p=0; 00483 p++; 00484 00485 GWEN_DB_SetCharValue(xio->dbStatusIn, GWEN_DB_FLAGS_OVERWRITE_VARS, "protocol", s); 00486 s=p; 00487 00488 /* read status code */ 00489 while(*p && isdigit((int)*p)) 00490 p++; 00491 if (*p) { 00492 *p=0; 00493 p++; 00494 } 00495 if (1!=sscanf(s, "%d", &code)) { 00496 DBG_ERROR(GWEN_LOGDOMAIN, "Bad request (status code \"%s\")", s); 00497 return GWEN_ERROR_INVALID; 00498 } 00499 GWEN_DB_SetIntValue(xio->dbStatusIn, GWEN_DB_FLAGS_OVERWRITE_VARS, "code", code); 00500 s=p; 00501 00502 /* read text */ 00503 GWEN_DB_SetCharValue(xio->dbStatusIn, GWEN_DB_FLAGS_OVERWRITE_VARS, "text", s); 00504 00505 return 0; 00506 } 00507 00508 00509 00510 int GWEN_SyncIo_Http_ParseCommand(GWEN_SYNCIO *sio, const char *buffer) { 00511 GWEN_SYNCIO_HTTP *xio; 00512 char *tmp; 00513 char *p; 00514 char *s; 00515 00516 assert(sio); 00517 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_HTTP, sio); 00518 assert(xio); 00519 00520 tmp=strdup(buffer); 00521 s=tmp; 00522 00523 /* read command */ 00524 p=strchr(s, ' '); 00525 if (!p) { 00526 DBG_ERROR(GWEN_LOGDOMAIN, 00527 "Bad format of HTTP request (%s)", buffer); 00528 free(tmp); 00529 return GWEN_ERROR_INVALID; 00530 } 00531 *p=0; 00532 p++; 00533 00534 GWEN_DB_SetCharValue(xio->dbCommandIn, GWEN_DB_FLAGS_OVERWRITE_VARS, "command", s); 00535 s=p; 00536 00537 /* read URL */ 00538 p=strchr(s, ' '); 00539 if (!p) { 00540 DBG_ERROR(GWEN_LOGDOMAIN, 00541 "Bad format of HTTP request (%s)", buffer); 00542 free(tmp); 00543 return GWEN_ERROR_INVALID; 00544 } 00545 *p=0; 00546 p++; 00547 00548 GWEN_DB_SetCharValue(xio->dbCommandIn, GWEN_DB_FLAGS_OVERWRITE_VARS, "url", s); 00549 s=p; 00550 00551 if (*s==0) { 00552 /* no protocol information follows, so we assume HTTP/0.9 */ 00553 DBG_ERROR(GWEN_LOGDOMAIN, "Bad request (not in HTTP>=1.0)"); 00554 free(tmp); 00555 return GWEN_ERROR_INVALID; 00556 } 00557 else { 00558 GWEN_DB_SetCharValue(xio->dbCommandIn, GWEN_DB_FLAGS_OVERWRITE_VARS, "protocol", s); 00559 } 00560 00561 free(tmp); 00562 return 0; 00563 } 00564 00565 00566 00567 int GWEN_SyncIo_Http_ReadStatus(GWEN_SYNCIO *sio) { 00568 GWEN_SYNCIO_HTTP *xio; 00569 GWEN_SYNCIO *baseIo; 00570 GWEN_BUFFER *tbuf; 00571 int rv; 00572 00573 assert(sio); 00574 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_HTTP, sio); 00575 assert(xio); 00576 00577 DBG_INFO(GWEN_LOGDOMAIN, "Reading status"); 00578 baseIo=GWEN_SyncIo_GetBaseIo(sio); 00579 assert(baseIo); 00580 00581 /* read a single line */ 00582 tbuf=GWEN_Buffer_new(0, 256, 0, 1); 00583 rv=GWEN_SyncIo_Http_ReadLine(sio, tbuf); 00584 if (rv<0) { 00585 DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv); 00586 GWEN_Buffer_free(tbuf); 00587 return rv; 00588 } 00589 00590 if (*GWEN_Buffer_GetStart(tbuf)==0) { 00591 DBG_INFO(GWEN_LOGDOMAIN, "Empty line received while reading status response, assuming EOF"); 00592 GWEN_Buffer_free(tbuf); 00593 return GWEN_ERROR_EOF; 00594 } 00595 00596 rv=GWEN_SyncIo_Http_ParseStatus(sio, GWEN_Buffer_GetStart(tbuf)); 00597 if (rv<0) { 00598 DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv); 00599 GWEN_Buffer_free(tbuf); 00600 return rv; 00601 } 00602 00603 GWEN_Buffer_free(tbuf); 00604 return 0; 00605 } 00606 00607 00608 00609 int GWEN_SyncIo_Http_ReadCommand(GWEN_SYNCIO *sio) { 00610 GWEN_SYNCIO_HTTP *xio; 00611 GWEN_SYNCIO *baseIo; 00612 GWEN_BUFFER *tbuf; 00613 int rv; 00614 00615 assert(sio); 00616 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_HTTP, sio); 00617 assert(xio); 00618 00619 DBG_INFO(GWEN_LOGDOMAIN, "Reading command"); 00620 baseIo=GWEN_SyncIo_GetBaseIo(sio); 00621 assert(baseIo); 00622 00623 /* read a single line */ 00624 tbuf=GWEN_Buffer_new(0, 256, 0, 1); 00625 rv=GWEN_SyncIo_Http_ReadLine(sio, tbuf); 00626 if (rv<0) { 00627 DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv); 00628 GWEN_Buffer_free(tbuf); 00629 return rv; 00630 } 00631 00632 rv=GWEN_SyncIo_Http_ParseCommand(sio, GWEN_Buffer_GetStart(tbuf)); 00633 if (rv<0) { 00634 DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv); 00635 GWEN_Buffer_free(tbuf); 00636 return rv; 00637 } 00638 00639 GWEN_Buffer_free(tbuf); 00640 return 0; 00641 } 00642 00643 00644 00645 int GWEN_SyncIo_Http_ParseHeader(GWEN_SYNCIO *sio, char *buf) { 00646 GWEN_SYNCIO_HTTP *xio; 00647 GWEN_SYNCIO *baseIo; 00648 char *p; 00649 const char *s; 00650 00651 assert(sio); 00652 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_HTTP, sio); 00653 assert(xio); 00654 00655 baseIo=GWEN_SyncIo_GetBaseIo(sio); 00656 assert(baseIo); 00657 00658 /* resolve line continuations */ 00659 p=buf; 00660 while(*p) { 00661 p=strchr(p, 10); 00662 if (p) { 00663 if (p[1]==32 || p[1]==9) 00664 /* found a continuation */ 00665 *p=32; 00666 p++; 00667 } 00668 } 00669 00670 /* parse every line */ 00671 p=buf; 00672 while(p && *p) { 00673 char *pNext; 00674 char *pVarBegin; 00675 char *pVarEnd; 00676 00677 /* skip blanks */ 00678 pNext=strchr(p, 10); 00679 if (pNext) { 00680 *pNext=0; 00681 pNext++; 00682 } 00683 while(*p && (*p==32 || *p==9)) 00684 p++; 00685 if (*p) { 00686 pVarBegin=p; 00687 while(*p && *p!=':' && *p>32 && *p<127) 00688 p++; 00689 pVarEnd=p; 00690 if (*p!=':') { 00691 DBG_INFO(GWEN_LOGDOMAIN, "No separator after variable name in received header"); 00692 return GWEN_ERROR_BAD_DATA; 00693 } 00694 *pVarEnd=0; 00695 p++; 00696 00697 while(*p && (*p==32 || *p==9)) 00698 p++; 00699 if (*p) 00700 GWEN_DB_SetCharValue(xio->dbHeaderIn, GWEN_PATH_FLAGS_CREATE_VAR, pVarBegin, p); 00701 } 00702 p=pNext; 00703 } 00704 00705 /* default next mode after reading the header is reading the body 00706 * (if any, but that will be checked later) */ 00707 xio->readMode=GWEN_SyncIo_Http_Mode_Body; 00708 00709 /* header received, now read some settings from it */ 00710 s=GWEN_DB_GetCharValue(xio->dbHeaderIn, "Transfer-Encoding", 0, 0); 00711 if (s && (-1!=GWEN_Text_ComparePattern(s, "*chunked*", 0))) { 00712 /* chunked encoding, this means next we have to read the chunksize */ 00713 DBG_DEBUG(GWEN_LOGDOMAIN, "Body is \"chunked\""); 00714 xio->currentReadChunkSize=-1; 00715 xio->readMode=GWEN_SyncIo_Http_Mode_ChunkSize; 00716 } 00717 00718 /* get size of body */ 00719 xio->currentReadBodySize=GWEN_DB_GetIntValue(xio->dbHeaderIn, "Content-Length", 0, -1); 00720 if (xio->currentReadBodySize==0) { 00721 /* no body */ 00722 GWEN_SyncIo_Http_SetReadIdle(sio); 00723 } 00724 else if (xio->currentReadBodySize==-1) { 00725 int rcode; 00726 00727 /* no length of body received, assume 0 in case of an error 00728 * This eliminates the bug where this module waits for 00729 * a timeout when receiving an error from a special server 00730 */ 00731 rcode=GWEN_DB_GetIntValue(xio->dbStatusIn, "code", 0, -1); 00732 if (rcode<0 || rcode>=300) { 00733 /* no body */ 00734 GWEN_SyncIo_Http_SetReadIdle(sio); 00735 } 00736 } 00737 00738 return 0; 00739 } 00740 00741 00742 00743 int GWEN_SyncIo_Http_ReadHeader(GWEN_SYNCIO *sio) { 00744 GWEN_SYNCIO_HTTP *xio; 00745 GWEN_SYNCIO *baseIo; 00746 GWEN_BUFFER *tbuf; 00747 int rv; 00748 uint32_t pos; 00749 int lines=0; 00750 00751 assert(sio); 00752 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_HTTP, sio); 00753 assert(xio); 00754 00755 DBG_INFO(GWEN_LOGDOMAIN, "Reading header"); 00756 baseIo=GWEN_SyncIo_GetBaseIo(sio); 00757 assert(baseIo); 00758 00759 /* we want to read a text line, so we can't have a transparent mode in the base layer */ 00760 GWEN_SyncIo_SubFlags(baseIo, GWEN_SYNCIO_FLAGS_TRANSPARENT); 00761 00762 /* read a single line */ 00763 tbuf=GWEN_Buffer_new(0, 256, 0, 1); 00764 pos=0; 00765 do { 00766 uint8_t *p; 00767 00768 GWEN_Buffer_AllocRoom(tbuf, 1024); 00769 p=(uint8_t*) GWEN_Buffer_GetPosPointer(tbuf); 00770 rv=GWEN_SyncIo_Read(baseIo, p, 1024); 00771 if (rv<0) { 00772 DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv); 00773 GWEN_Buffer_free(tbuf); 00774 return rv; 00775 } 00776 else if (rv>0) { 00777 GWEN_Buffer_IncrementPos(tbuf, rv); 00778 GWEN_Buffer_AdjustUsedBytes(tbuf); 00779 if (p[rv-1]==10) { 00780 uint32_t npos; 00781 00782 lines++; 00783 npos=GWEN_Buffer_GetPos(tbuf); 00784 if ((npos-pos)==1) { 00785 /* empty line, header finished */ 00786 break; 00787 } 00788 pos=npos; 00789 } 00790 } 00791 else if (rv==0) 00792 break; 00793 } while(rv>0); 00794 00795 if (lines<1) { 00796 DBG_ERROR(GWEN_LOGDOMAIN, "No header line received"); 00797 GWEN_Buffer_free(tbuf); 00798 return GWEN_ERROR_EOF; 00799 } 00800 00801 rv=GWEN_SyncIo_Http_ParseHeader(sio, GWEN_Buffer_GetStart(tbuf)); 00802 if (rv<0) { 00803 DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv); 00804 GWEN_Buffer_free(tbuf); 00805 return rv; 00806 } 00807 00808 GWEN_Buffer_free(tbuf); 00809 return 0; 00810 } 00811 00812 00813 00814 int GWEN_SyncIo_Http_ReadChunkSize(GWEN_SYNCIO *sio) { 00815 GWEN_SYNCIO_HTTP *xio; 00816 GWEN_SYNCIO *baseIo; 00817 GWEN_BUFFER *tbuf; 00818 int rv; 00819 int csize; 00820 00821 assert(sio); 00822 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_HTTP, sio); 00823 assert(xio); 00824 00825 DBG_INFO(GWEN_LOGDOMAIN, "Reading chunksize"); 00826 baseIo=GWEN_SyncIo_GetBaseIo(sio); 00827 assert(baseIo); 00828 00829 /* read a single line */ 00830 tbuf=GWEN_Buffer_new(0, 256, 0, 1); 00831 rv=GWEN_SyncIo_Http_ReadLine(sio, tbuf); 00832 if (rv<0) { 00833 DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv); 00834 GWEN_Buffer_free(tbuf); 00835 return rv; 00836 } 00837 00838 if (*GWEN_Buffer_GetStart(tbuf)==0) { 00839 GWEN_Buffer_Reset(tbuf); 00840 rv=GWEN_SyncIo_Http_ReadLine(sio, tbuf); 00841 if (rv<0) { 00842 DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv); 00843 GWEN_Buffer_free(tbuf); 00844 return rv; 00845 } 00846 } 00847 00848 if (1!=sscanf(GWEN_Buffer_GetStart(tbuf), "%x", &csize)) { 00849 DBG_ERROR(GWEN_LOGDOMAIN, "Bad data received (invalid chunksize specifier: [%s])", 00850 GWEN_Buffer_GetStart(tbuf)); 00851 GWEN_Buffer_free(tbuf); 00852 return GWEN_ERROR_BAD_DATA; 00853 } 00854 00855 xio->currentReadChunkSize=csize; 00856 00857 GWEN_Buffer_free(tbuf); 00858 return 0; 00859 } 00860 00861 00862 00863 int GWEN_SyncIo_Http_ReadChunk(GWEN_SYNCIO *sio, uint8_t *buffer, uint32_t size) { 00864 GWEN_SYNCIO_HTTP *xio; 00865 GWEN_SYNCIO *baseIo; 00866 int rv; 00867 00868 assert(sio); 00869 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_HTTP, sio); 00870 assert(xio); 00871 00872 DBG_DEBUG(GWEN_LOGDOMAIN, "Reading chunk (%d bytes)", (int) size); 00873 baseIo=GWEN_SyncIo_GetBaseIo(sio); 00874 assert(baseIo); 00875 00876 /* we want to read binary data transparently */ 00877 GWEN_SyncIo_AddFlags(baseIo, GWEN_SYNCIO_FLAGS_TRANSPARENT); 00878 00879 if (size>xio->currentReadChunkSize) 00880 size=xio->currentReadChunkSize; 00881 00882 rv=GWEN_SyncIo_Read(baseIo, buffer, size); 00883 if (rv<0) { 00884 DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv); 00885 return rv; 00886 } 00887 00888 xio->currentReadChunkSize-=rv; 00889 if (xio->currentReadBodySize>0) 00890 xio->currentReadBodySize-=rv; 00891 00892 if (xio->currentReadChunkSize==0) { 00893 int rv2; 00894 GWEN_BUFFER *tbuf; 00895 00896 /* chunk finished, read trailing CR/LF */ 00897 tbuf=GWEN_Buffer_new(0, 256, 0, 1); 00898 rv2=GWEN_SyncIo_Http_ReadLine(sio, tbuf); 00899 if (rv2<0) { 00900 DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv2); 00901 GWEN_Buffer_free(tbuf); 00902 return rv2; 00903 } 00904 GWEN_Buffer_free(tbuf); 00905 00906 DBG_DEBUG(GWEN_LOGDOMAIN, "Chunk finished."); 00907 00908 /* change read mode */ 00909 xio->readMode=GWEN_SyncIo_Http_Mode_ChunkSize; 00910 } 00911 00912 return rv; 00913 } 00914 00915 00916 00917 int GWEN_SyncIo_Http_ReadBody(GWEN_SYNCIO *sio, uint8_t *buffer, uint32_t size) { 00918 GWEN_SYNCIO_HTTP *xio; 00919 GWEN_SYNCIO *baseIo; 00920 int rv; 00921 00922 assert(size); 00923 00924 assert(sio); 00925 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_HTTP, sio); 00926 assert(xio); 00927 00928 DBG_INFO(GWEN_LOGDOMAIN, "Reading body"); 00929 baseIo=GWEN_SyncIo_GetBaseIo(sio); 00930 assert(baseIo); 00931 00932 /* we want to read binary data transparently */ 00933 GWEN_SyncIo_AddFlags(baseIo, GWEN_SYNCIO_FLAGS_TRANSPARENT); 00934 00935 if ((xio->currentReadBodySize>=0) && 00936 (size>xio->currentReadBodySize)) { 00937 DBG_INFO(GWEN_LOGDOMAIN, "Adjusting read body size from %d to %d", 00938 size, xio->currentReadBodySize); 00939 size=xio->currentReadBodySize; 00940 } 00941 00942 rv=GWEN_SyncIo_Read(baseIo, buffer, size); 00943 if (rv<0) { 00944 DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv); 00945 return rv; 00946 } 00947 00948 if (xio->currentReadBodySize>=0) 00949 xio->currentReadBodySize-=rv; 00950 00951 if (xio->currentReadBodySize==0) 00952 /* body finished, change read mode */ 00953 GWEN_SyncIo_Http_SetReadIdle(sio); 00954 00955 return rv; 00956 } 00957 00958 00959 00960 int GWEN_SyncIo_Http_WriteCommand(GWEN_SYNCIO *sio) { 00961 GWEN_SYNCIO_HTTP *xio; 00962 GWEN_SYNCIO *baseIo; 00963 int rv; 00964 const char *s; 00965 GWEN_BUFFER *tbuf; 00966 00967 assert(sio); 00968 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_HTTP, sio); 00969 assert(xio); 00970 00971 baseIo=GWEN_SyncIo_GetBaseIo(sio); 00972 assert(baseIo); 00973 00974 /* we will construct the line including CR/LF ourselves */ 00975 GWEN_SyncIo_AddFlags(baseIo, GWEN_SYNCIO_FLAGS_TRANSPARENT); 00976 00977 tbuf=GWEN_Buffer_new(0, 256, 0, 1); 00978 00979 s=GWEN_DB_GetCharValue(xio->dbCommandOut, "command", 0, "GET"); 00980 GWEN_Buffer_AppendString(tbuf, s); 00981 GWEN_Buffer_AppendString(tbuf, " "); 00982 00983 s=GWEN_DB_GetCharValue(xio->dbCommandOut, "url", 0, "/"); 00984 GWEN_Buffer_AppendString(tbuf, s); 00985 GWEN_Buffer_AppendString(tbuf, " "); 00986 00987 s=GWEN_DB_GetCharValue(xio->dbCommandOut, "protocol", 0, "HTTP/1.0"); 00988 GWEN_Buffer_AppendString(tbuf, s); 00989 GWEN_Buffer_AppendString(tbuf, "\r\n"); 00990 00991 /* write */ 00992 rv=GWEN_SyncIo_WriteForced(baseIo, 00993 (const uint8_t*) GWEN_Buffer_GetStart(tbuf), 00994 GWEN_Buffer_GetUsedBytes(tbuf)); 00995 if (rv<0) { 00996 DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv); 00997 GWEN_Buffer_free(tbuf); 00998 return rv; 00999 } 01000 01001 GWEN_Buffer_free(tbuf); 01002 return 0; 01003 } 01004 01005 01006 01007 int GWEN_SyncIo_Http_WriteStatus(GWEN_SYNCIO *sio) { 01008 GWEN_SYNCIO_HTTP *xio; 01009 GWEN_SYNCIO *baseIo; 01010 int rv; 01011 const char *s; 01012 GWEN_BUFFER *tbuf; 01013 char numbuf[32]; 01014 int i; 01015 01016 assert(sio); 01017 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_HTTP, sio); 01018 assert(xio); 01019 01020 baseIo=GWEN_SyncIo_GetBaseIo(sio); 01021 assert(baseIo); 01022 01023 /* we will construct the line including CR/LF ourselves */ 01024 GWEN_SyncIo_AddFlags(baseIo, GWEN_SYNCIO_FLAGS_TRANSPARENT); 01025 01026 tbuf=GWEN_Buffer_new(0, 256, 0, 1); 01027 01028 s=GWEN_DB_GetCharValue(xio->dbStatusOut, "protocol", 0, "HTTP/1.0"); 01029 GWEN_Buffer_AppendString(tbuf, s); 01030 GWEN_Buffer_AppendString(tbuf, " "); 01031 01032 i=GWEN_DB_GetIntValue(xio->dbStatusOut, "code", 0, -1); 01033 if (i==-1) { 01034 DBG_INFO(GWEN_LOGDOMAIN, "Missing status code"); 01035 GWEN_Buffer_free(tbuf); 01036 return GWEN_ERROR_NO_DATA; 01037 } 01038 snprintf(numbuf, sizeof(numbuf), "%d ", i); 01039 GWEN_Buffer_AppendString(tbuf, numbuf); 01040 01041 s=GWEN_DB_GetCharValue(xio->dbStatusOut, "text", 0, "No text."); 01042 GWEN_Buffer_AppendString(tbuf, s); 01043 GWEN_Buffer_AppendString(tbuf, "\r\n"); 01044 01045 /* write */ 01046 rv=GWEN_SyncIo_WriteForced(baseIo, 01047 (const uint8_t*) GWEN_Buffer_GetStart(tbuf), 01048 GWEN_Buffer_GetUsedBytes(tbuf)); 01049 if (rv<0) { 01050 DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv); 01051 GWEN_Buffer_free(tbuf); 01052 return rv; 01053 } 01054 01055 GWEN_Buffer_free(tbuf); 01056 return 0; 01057 } 01058 01059 01060 01061 int GWEN_SyncIo_Http_WriteHeader(GWEN_SYNCIO *sio) { 01062 GWEN_SYNCIO_HTTP *xio; 01063 GWEN_SYNCIO *baseIo; 01064 int i; 01065 GWEN_DB_NODE *dbVar; 01066 GWEN_BUFFER *tbuf; 01067 int rv; 01068 01069 assert(sio); 01070 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_HTTP, sio); 01071 assert(xio); 01072 01073 baseIo=GWEN_SyncIo_GetBaseIo(sio); 01074 assert(baseIo); 01075 01076 /* we will construct the line including CR/LF ourselves */ 01077 GWEN_SyncIo_AddFlags(baseIo, GWEN_SYNCIO_FLAGS_TRANSPARENT); 01078 01079 /* default next mode after writing the header is writing the body 01080 * (if any, but that will be checked later) */ 01081 xio->writeMode=GWEN_SyncIo_Http_Mode_Body; 01082 01083 tbuf=GWEN_Buffer_new(0, 256, 0, 1); 01084 01085 i=GWEN_DB_GetIntValue(xio->dbHeaderOut, "Content-Length", 0, -1); 01086 xio->currentWriteBodySize=i; 01087 01088 dbVar=GWEN_DB_GetFirstVar(xio->dbHeaderOut); 01089 while (dbVar) { 01090 GWEN_DB_NODE *dbVal; 01091 01092 /* only handle first value */ 01093 dbVal=GWEN_DB_GetFirstValue(dbVar); 01094 if (dbVal) { 01095 GWEN_DB_NODE_TYPE vtype; 01096 01097 vtype=GWEN_DB_GetValueType(dbVal); 01098 if (vtype==GWEN_DB_NodeType_ValueChar) { 01099 const char *s; 01100 01101 GWEN_Buffer_AppendString(tbuf, GWEN_DB_VariableName(dbVar)); 01102 GWEN_Buffer_AppendString(tbuf, ":"); 01103 s=GWEN_DB_GetCharValueFromNode(dbVal); 01104 if (s) 01105 GWEN_Buffer_AppendString(tbuf, s); 01106 GWEN_Buffer_AppendString(tbuf, "\r\n"); 01107 01108 if (strcasecmp(GWEN_DB_VariableName(dbVar), "Transfer-Encoding")==0) { 01109 if (s && (-1!=GWEN_Text_ComparePattern(s, "*chunked*", 0))) { 01110 /* chunked encoding, this means next we have to write the chunksize */ 01111 xio->writeMode=GWEN_SyncIo_Http_Mode_ChunkSize; 01112 } 01113 } 01114 } 01115 else if (vtype==GWEN_DB_NodeType_ValueInt) { 01116 i=GWEN_DB_GetIntValueFromNode(dbVal); 01117 if (i!=-1 || strcasecmp(GWEN_DB_VariableName(dbVar), "Content-Length")==0) { 01118 char numbuf[32]; 01119 01120 /* don't write body size of -1 */ 01121 GWEN_Buffer_AppendString(tbuf, GWEN_DB_VariableName(dbVar)); 01122 GWEN_Buffer_AppendString(tbuf, ":"); 01123 snprintf(numbuf, sizeof(numbuf), "%d", i); 01124 GWEN_Buffer_AppendString(tbuf, numbuf); 01125 GWEN_Buffer_AppendString(tbuf, "\r\n"); 01126 } 01127 } 01128 else { 01129 DBG_INFO(GWEN_LOGDOMAIN, "Variable type %d of var [%s] not supported", 01130 vtype, GWEN_DB_VariableName(dbVar)); 01131 return GWEN_ERROR_BAD_DATA; 01132 } 01133 } 01134 dbVar=GWEN_DB_GetNextVar(dbVar); 01135 } 01136 01137 /* finalize header */ 01138 GWEN_Buffer_AppendString(tbuf, "\r\n"); 01139 01140 /* write */ 01141 rv=GWEN_SyncIo_WriteForced(baseIo, 01142 (const uint8_t*) GWEN_Buffer_GetStart(tbuf), 01143 GWEN_Buffer_GetUsedBytes(tbuf)); 01144 if (rv<0) { 01145 DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv); 01146 GWEN_Buffer_free(tbuf); 01147 return rv; 01148 } 01149 GWEN_Buffer_free(tbuf); 01150 01151 if (xio->currentWriteBodySize==0) 01152 GWEN_SyncIo_Http_SetWriteIdle(sio); 01153 01154 return 0; 01155 } 01156 01157 01158 01159 int GWEN_SyncIo_Http_WriteChunkSize(GWEN_SYNCIO *sio, uint32_t size) { 01160 GWEN_SYNCIO_HTTP *xio; 01161 GWEN_SYNCIO *baseIo; 01162 int rv; 01163 char numbuf[32]; 01164 01165 assert(sio); 01166 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_HTTP, sio); 01167 assert(xio); 01168 01169 baseIo=GWEN_SyncIo_GetBaseIo(sio); 01170 assert(baseIo); 01171 01172 /* we will construct the line including CR/LF ourselves */ 01173 GWEN_SyncIo_AddFlags(baseIo, GWEN_SYNCIO_FLAGS_TRANSPARENT); 01174 01175 snprintf(numbuf, sizeof(numbuf)-1, "%x\r\n", size); 01176 numbuf[sizeof(numbuf)-1]=0; 01177 01178 rv=GWEN_SyncIo_WriteForced(baseIo, (const uint8_t*) numbuf, strlen(numbuf)); 01179 if (rv<0) { 01180 DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv); 01181 return rv; 01182 } 01183 01184 return 0; 01185 } 01186 01187 01188 01189 void GWEN_SyncIo_Http_SetWriteIdle(GWEN_SYNCIO *sio) { 01190 GWEN_SYNCIO_HTTP *xio; 01191 01192 assert(sio); 01193 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_HTTP, sio); 01194 assert(xio); 01195 01196 xio->writeMode=GWEN_SyncIo_Http_Mode_Idle; 01197 GWEN_DB_ClearGroup(xio->dbStatusOut, NULL); 01198 } 01199 01200 01201 01202 01203 GWEN_DB_NODE *GWEN_SyncIo_Http_GetDbCommandIn(const GWEN_SYNCIO *sio) { 01204 GWEN_SYNCIO_HTTP *xio; 01205 01206 assert(sio); 01207 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_HTTP, sio); 01208 assert(xio); 01209 01210 return xio->dbCommandIn; 01211 } 01212 01213 01214 01215 GWEN_DB_NODE *GWEN_SyncIo_Http_GetDbStatusIn(const GWEN_SYNCIO *sio) { 01216 GWEN_SYNCIO_HTTP *xio; 01217 01218 assert(sio); 01219 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_HTTP, sio); 01220 assert(xio); 01221 01222 return xio->dbStatusIn; 01223 } 01224 01225 01226 01227 GWEN_DB_NODE *GWEN_SyncIo_Http_GetDbHeaderIn(const GWEN_SYNCIO *sio) { 01228 GWEN_SYNCIO_HTTP *xio; 01229 01230 assert(sio); 01231 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_HTTP, sio); 01232 assert(xio); 01233 01234 return xio->dbHeaderIn; 01235 } 01236 01237 01238 01239 GWEN_DB_NODE *GWEN_SyncIo_Http_GetDbCommandOut(const GWEN_SYNCIO *sio) { 01240 GWEN_SYNCIO_HTTP *xio; 01241 01242 assert(sio); 01243 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_HTTP, sio); 01244 assert(xio); 01245 01246 return xio->dbCommandOut; 01247 } 01248 01249 01250 01251 GWEN_DB_NODE *GWEN_SyncIo_Http_GetDbStatusOut(const GWEN_SYNCIO *sio) { 01252 GWEN_SYNCIO_HTTP *xio; 01253 01254 assert(sio); 01255 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_HTTP, sio); 01256 assert(xio); 01257 01258 return xio->dbStatusOut; 01259 } 01260 01261 01262 01263 GWEN_DB_NODE *GWEN_SyncIo_Http_GetDbHeaderOut(const GWEN_SYNCIO *sio) { 01264 GWEN_SYNCIO_HTTP *xio; 01265 01266 assert(sio); 01267 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_HTTP, sio); 01268 assert(xio); 01269 01270 return xio->dbHeaderOut; 01271 } 01272 01273 01274 01275 01276 int GWEN_SyncIo_Http_RecvBody(GWEN_SYNCIO *sio, GWEN_BUFFER *buf) { 01277 GWEN_SYNCIO_HTTP *xio; 01278 int rv; 01279 int code=0; 01280 int firstRead=1; 01281 int bodySize=-1; 01282 int bytesRead=0; 01283 uint32_t pid; 01284 01285 assert(sio); 01286 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_HTTP, sio); 01287 assert(xio); 01288 01289 pid=GWEN_Gui_ProgressStart(GWEN_GUI_PROGRESS_DELAY | 01290 GWEN_GUI_PROGRESS_SHOW_ABORT | 01291 GWEN_GUI_PROGRESS_ALLOW_EMBED | 01292 GWEN_GUI_PROGRESS_SHOW_PROGRESS, 01293 I18N("Network Operation"), 01294 I18N("Receiving data"), 01295 0, 01296 0); 01297 01298 01299 /* recv packet (this reads the HTTP body) */ 01300 for (;;) { 01301 uint8_t *p; 01302 uint32_t l; 01303 01304 rv=GWEN_Buffer_AllocRoom(buf, 1024); 01305 if (rv<0) { 01306 DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv); 01307 GWEN_Gui_ProgressEnd(pid); 01308 return rv; 01309 } 01310 01311 p=(uint8_t*) GWEN_Buffer_GetPosPointer(buf); 01312 l=GWEN_Buffer_GetMaxUnsegmentedWrite(buf); 01313 do { 01314 rv=GWEN_SyncIo_Read(sio, p, l-1); 01315 } while(rv==GWEN_ERROR_INTERRUPTED); 01316 01317 if (rv==0) 01318 break; 01319 else if (rv<0) { 01320 if (rv==GWEN_ERROR_EOF) { 01321 if (bodySize!=-1 && bytesRead<bodySize) { 01322 DBG_ERROR(GWEN_LOGDOMAIN, 01323 "EOF met prematurely (%d < %d)", 01324 bytesRead, bodySize); 01325 GWEN_Gui_ProgressEnd(pid); 01326 return GWEN_ERROR_EOF; 01327 } 01328 } 01329 else { 01330 DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv); 01331 /*return rv;*/ 01332 break; 01333 } 01334 } 01335 else { 01336 GWEN_Buffer_IncrementPos(buf, rv); 01337 GWEN_Buffer_AdjustUsedBytes(buf); 01338 if (firstRead) { 01339 GWEN_DB_NODE *db; 01340 01341 db=GWEN_SyncIo_Http_GetDbHeaderIn(sio); 01342 bodySize=GWEN_DB_GetIntValue(db, "Content-length", 0, -1); 01343 01344 if (bodySize!=-1) 01345 GWEN_Gui_ProgressSetTotal(pid, bodySize); 01346 } 01347 bytesRead+=rv; 01348 01349 /* advance progress bar */ 01350 rv=GWEN_Gui_ProgressAdvance(pid, bytesRead); 01351 if (rv==GWEN_ERROR_USER_ABORTED) { 01352 DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv); 01353 GWEN_Gui_ProgressEnd(pid); 01354 return rv; 01355 } 01356 } 01357 01358 if (bodySize!=-1 && bytesRead>=bodySize) { 01359 break; 01360 } 01361 01362 firstRead=0; 01363 } 01364 GWEN_Gui_ProgressEnd(pid); 01365 01366 if (rv<0) { 01367 if (GWEN_Buffer_GetUsedBytes(buf)) { 01368 /* data received, check for common error codes */ 01369 if (rv==GWEN_ERROR_EOF || rv==GWEN_ERROR_IO || rv==GWEN_ERROR_SSL) { 01370 DBG_INFO(GWEN_LOGDOMAIN, 01371 "We received an error, but we still got data, " 01372 "so we ignore the error here"); 01373 } 01374 else { 01375 DBG_INFO(GWEN_LOGDOMAIN, "No message received (%d)", rv); 01376 GWEN_Gui_ProgressLog(0, 01377 GWEN_LoggerLevel_Error, 01378 I18N("No message received")); 01379 return rv; 01380 } 01381 } 01382 else { 01383 DBG_INFO(GWEN_LOGDOMAIN, "No message received (%d)", rv); 01384 GWEN_Gui_ProgressLog(0, 01385 GWEN_LoggerLevel_Error, 01386 I18N("No message received")); 01387 return rv; 01388 } 01389 } 01390 01391 if (GWEN_SyncIo_GetFlags(sio) & GWEN_SYNCIO_FLAGS_PASSIVE) 01392 code=0; 01393 else { 01394 code=GWEN_DB_GetIntValue(xio->dbStatusIn, "code", 0, 0); 01395 if (code) { 01396 const char *s; 01397 01398 s=GWEN_DB_GetCharValue(xio->dbStatusIn, "text", 0, NULL); 01399 DBG_DEBUG(GWEN_LOGDOMAIN, "HTTP-Status: %d (%s)", 01400 code, s?s:"- no text -"); 01401 GWEN_Gui_ProgressLog2(0, GWEN_LoggerLevel_Info, 01402 I18N("HTTP-Status: %d (%s)"), 01403 code, s?s:I18N("- no details -)")); 01404 } 01405 else { 01406 DBG_ERROR(GWEN_LOGDOMAIN, "No HTTP status code received"); 01407 GWEN_Gui_ProgressLog(0, 01408 GWEN_LoggerLevel_Error, 01409 I18N("No HTTP status code received")); 01410 code=GWEN_ERROR_BAD_DATA; 01411 } 01412 } 01413 01414 return code; 01415 } 01416 01417 01418 01419 int GWEN_SyncIo_Http_RecvBodyToSio(GWEN_SYNCIO *sio, GWEN_SYNCIO *sout) { 01420 GWEN_SYNCIO_HTTP *xio; 01421 int rv; 01422 int code=0; 01423 int firstRead=1; 01424 int bodySize=-1; 01425 int bytesRead=0; 01426 uint32_t pid; 01427 01428 assert(sio); 01429 xio=GWEN_INHERIT_GETDATA(GWEN_SYNCIO, GWEN_SYNCIO_HTTP, sio); 01430 assert(xio); 01431 01432 pid=GWEN_Gui_ProgressStart(GWEN_GUI_PROGRESS_DELAY | 01433 GWEN_GUI_PROGRESS_SHOW_ABORT | 01434 GWEN_GUI_PROGRESS_ALLOW_EMBED | 01435 GWEN_GUI_PROGRESS_SHOW_PROGRESS, 01436 I18N("Network Operation"), 01437 I18N("Receiving data"), 01438 0, 01439 0); 01440 01441 /* recv packet (this reads the HTTP body) */ 01442 for (;;) { 01443 uint8_t *p; 01444 uint32_t l; 01445 uint8_t rbuf[1024]; 01446 01447 p=rbuf; 01448 l=sizeof(rbuf); 01449 01450 do { 01451 rv=GWEN_SyncIo_Read(sio, p, l-1); 01452 } while(rv==GWEN_ERROR_INTERRUPTED); 01453 01454 if (rv==0) 01455 break; 01456 else if (rv<0) { 01457 if (rv==GWEN_ERROR_EOF) { 01458 if (bodySize!=-1 && bytesRead<bodySize) { 01459 DBG_ERROR(GWEN_LOGDOMAIN, 01460 "EOF met prematurely (%d < %d)", 01461 bytesRead, bodySize); 01462 GWEN_Gui_ProgressEnd(pid); 01463 return GWEN_ERROR_EOF; 01464 } 01465 } 01466 else { 01467 DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv); 01468 /*return rv;*/ 01469 break; 01470 } 01471 } 01472 else { 01473 int rv2; 01474 01475 rv2=GWEN_SyncIo_WriteForced(sout, rbuf, rv); 01476 if (rv2<0) { 01477 DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv2); 01478 GWEN_Gui_ProgressEnd(pid); 01479 return rv2; 01480 } 01481 if (firstRead) { 01482 GWEN_DB_NODE *db; 01483 01484 db=GWEN_SyncIo_Http_GetDbHeaderIn(sio); 01485 bodySize=GWEN_DB_GetIntValue(db, "Content-length", 0, -1); 01486 01487 if (bodySize!=-1) 01488 GWEN_Gui_ProgressSetTotal(pid, bodySize); 01489 } 01490 bytesRead+=rv; 01491 01492 /* advance progress bar */ 01493 rv=GWEN_Gui_ProgressAdvance(pid, bytesRead); 01494 if (rv==GWEN_ERROR_USER_ABORTED) { 01495 DBG_INFO(GWEN_LOGDOMAIN, "here (%d)", rv); 01496 GWEN_Gui_ProgressEnd(pid); 01497 return rv; 01498 } 01499 } 01500 01501 if (bodySize!=-1 && bytesRead>=bodySize) { 01502 break; 01503 } 01504 firstRead=0; 01505 } 01506 GWEN_Gui_ProgressEnd(pid); 01507 01508 01509 if (rv<0) { 01510 if (bytesRead) { 01511 /* data received, check for common error codes */ 01512 if (rv==GWEN_ERROR_EOF || rv==GWEN_ERROR_IO || rv==GWEN_ERROR_SSL) { 01513 DBG_INFO(GWEN_LOGDOMAIN, 01514 "We received an error, but we still got data, " 01515 "so we ignore the error here"); 01516 } 01517 else { 01518 DBG_INFO(GWEN_LOGDOMAIN, "No message received (%d)", rv); 01519 GWEN_Gui_ProgressLog(0, 01520 GWEN_LoggerLevel_Error, 01521 I18N("No message received")); 01522 return rv; 01523 } 01524 } 01525 else { 01526 DBG_INFO(GWEN_LOGDOMAIN, "No message received (%d)", rv); 01527 GWEN_Gui_ProgressLog(0, 01528 GWEN_LoggerLevel_Error, 01529 I18N("No message received")); 01530 return rv; 01531 } 01532 } 01533 01534 if (GWEN_SyncIo_GetFlags(sio) & GWEN_SYNCIO_FLAGS_PASSIVE) 01535 code=0; 01536 else { 01537 code=GWEN_DB_GetIntValue(xio->dbStatusIn, "code", 0, 0); 01538 if (code) { 01539 const char *s; 01540 01541 s=GWEN_DB_GetCharValue(xio->dbStatusIn, "text", 0, NULL); 01542 DBG_DEBUG(GWEN_LOGDOMAIN, "HTTP-Status: %d (%s)", 01543 code, s?s:"- no text -"); 01544 GWEN_Gui_ProgressLog2(0, GWEN_LoggerLevel_Info, 01545 I18N("HTTP-Status: %d (%s)"), 01546 code, s?s:I18N("- no details -)")); 01547 } 01548 else { 01549 DBG_ERROR(GWEN_LOGDOMAIN, "No HTTP status code received"); 01550 GWEN_Gui_ProgressLog(0, 01551 GWEN_LoggerLevel_Error, 01552 I18N("No HTTP status code received")); 01553 code=GWEN_ERROR_BAD_DATA; 01554 } 01555 } 01556 01557 return code; 01558 } 01559 01560 01561 01562