source: trunk/base/src/pextlib1.0/curl.c @ 30087

Last change on this file since 30087 was 30087, checked in by afb@…, 12 years ago

add optional --user-agent parameter to curl

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 22.6 KB
Line 
1/*
2 * curl.c
3 * $Id: curl.c 30087 2007-10-19 12:14:15Z afb@macports.org $
4 *
5 * Copyright (c) 2005 Paul Guyot, MacPorts Team.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of MacPorts Team nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33#if HAVE_CONFIG_H
34#include <config.h>
35#endif
36
37#include <stdio.h>
38#include <stdlib.h>
39#include <ctype.h>
40#include <errno.h>
41
42#if HAVE_STRING_H
43#include <string.h>
44#endif
45
46#ifdef HAVE_UTIME_H
47#include <utime.h>
48#endif
49
50#include <curl/curl.h>
51
52#include <tcl.h>
53#include <tclDecls.h>
54
55#include "curl.h"
56
57/* Avoid a warning with Tcl < 8.4, even if Tcl_GetIndexFromObj's tablePtr
58probably isn't modified. */
59#if (TCL_MAJOR_VERSION > 8) || (TCL_MINOR_VERSION >= 4)
60typedef CONST char* tableEntryString;
61#else
62typedef char* tableEntryString;
63#endif
64
65/*
66 * Some compiled-in constants that we may wish to change later, given more
67 * empirical data.  These represent "best guess" values for now.
68 */
69#define _CURL_CONNECTION_TIMEOUT        ((long)(5 * 60))        /* 5 minutes */
70#define _CURL_MINIMUM_XFER_SPEED        ((long)1024)            /* 1Kb/sec */
71#define _CURL_MINIMUM_XFER_TIMEOUT      ((long)(10 * 60))       /* 10 minutes */
72
73/* ========================================================================= **
74 * Definitions
75 * ========================================================================= */
76#pragma mark Definitions
77
78/* ------------------------------------------------------------------------- **
79 * Prototypes
80 * ------------------------------------------------------------------------- */
81int SetResultFromCurlErrorCode(Tcl_Interp* interp, CURLcode inErrorCode);
82int CurlFetchCmd(Tcl_Interp* interp, int objc, Tcl_Obj* CONST objv[]);
83int CurlIsNewerCmd(Tcl_Interp* interp, int objc, Tcl_Obj* CONST objv[]);
84
85/* ========================================================================= **
86 * Entry points
87 * ========================================================================= */
88#pragma mark -
89#pragma mark Entry points
90
91/**
92 * Set the result if a libcurl error occurred return TCL_ERROR.
93 * Otherwise, set the result to "" and return TCL_OK.
94 *
95 * @param interp                pointer to the interpreter.
96 * @param inErrorCode   code of the error.
97 * @return TCL_OK if inErrorCode is 0, TCL_ERROR otherwise.
98 */
99int
100SetResultFromCurlErrorCode(Tcl_Interp* interp, CURLcode inErrorCode)
101{
102        int theResult;
103
104        switch(inErrorCode)
105        {
106                case CURLE_OK:
107                        Tcl_SetResult(interp, "", TCL_STATIC);
108                        theResult = TCL_OK;
109                        break;
110               
111                default:
112#ifdef HAVE_CURL_EASY_STRERROR
113                        Tcl_SetResult(interp, (char*) curl_easy_strerror(inErrorCode), TCL_VOLATILE);
114#else
115                        {
116                                char theErrorString[512];
117                                (void) snprintf(theErrorString, sizeof(theErrorString),
118                                        "curl error %i", inErrorCode);
119                                Tcl_SetResult(interp, theErrorString, TCL_VOLATILE);                           
120                        }
121#endif
122                        theResult = TCL_ERROR;
123        }
124       
125        return theResult;
126}
127
128/**
129 * curl fetch subcommand entry point.
130 *
131 * syntax: curl fetch [-v] [--disable-epsv] [--ignore-ssl-cert] [--remote-time] [-u userpass] [--effective-url lasturlvar] url filename
132 *
133 * @param interp                current interpreter
134 * @param objc                  number of parameters
135 * @param objv                  parameters
136 */
137int
138CurlFetchCmd(Tcl_Interp* interp, int objc, Tcl_Obj* CONST objv[])
139{
140        int theResult = TCL_OK;
141        CURL* theHandle = NULL;
142        FILE* theFile = NULL;
143
144        do {
145                long theResponseCode = 0;
146                int noprogress = 1;
147                int useepsv = 1;
148                int ignoresslcert = 0;
149                int remotetime = 0;
150                const char* theUserPassString = NULL;
151                const char* effectiveURLVarName = NULL;
152                char* effectiveURL = NULL;
153                char* userAgent = "MacPorts libcurl/" LIBCURL_VERSION;
154                int optioncrsr;
155                int lastoption;
156                const char* theURL;
157                const char* theFilePath;
158                long theFileTime = 0;
159                CURLcode theCurlCode;
160                struct curl_slist *headers = NULL;
161               
162                /* we might have options and then the url and the file */
163                /* let's process the options first */
164               
165                optioncrsr = 2;
166                lastoption = objc - 3;
167                while (optioncrsr <= lastoption) {
168                        /* get the option */
169                        const char* theOption = Tcl_GetString(objv[optioncrsr]);
170                       
171                        if (strcmp(theOption, "-v") == 0) {
172                                noprogress = 0;
173                        } else if (strcmp(theOption, "--disable-epsv") == 0) {
174                                useepsv = 0;
175                        } else if (strcmp(theOption, "--ignore-ssl-cert") == 0) {
176                                ignoresslcert = 1;
177                        } else if (strcmp(theOption, "--remote-time") == 0) {
178                                remotetime = 1;
179                        } else if (strcmp(theOption, "-u") == 0) {
180                                /* check we also have the parameter */
181                                if (optioncrsr < lastoption) {
182                                        optioncrsr++;
183                                        theUserPassString = Tcl_GetString(objv[optioncrsr]);
184                                } else {
185                                        Tcl_SetResult(interp,
186                                                "curl fetch: -u option requires a parameter",
187                                                TCL_STATIC);
188                                        theResult = TCL_ERROR;
189                                        break;                                 
190                                }
191                        } else if (strcmp(theOption, "--effective-url") == 0) {
192                                /* check we also have the parameter */
193                                if (optioncrsr < lastoption) {
194                                        optioncrsr++;
195                                        effectiveURLVarName = Tcl_GetString(objv[optioncrsr]);
196                                } else {
197                                        Tcl_SetResult(interp,
198                                                "curl fetch: --effective-url option requires a parameter",
199                                                TCL_STATIC);
200                                        theResult = TCL_ERROR;
201                                        break;                                 
202                                }
203                        } else if (strcmp(theOption, "--user-agent") == 0) {
204                                /* check we also have the parameter */
205                                if (optioncrsr < lastoption) {
206                                        optioncrsr++;
207                                        userAgent = Tcl_GetString(objv[optioncrsr]);
208                                } else {
209                                        Tcl_SetResult(interp,
210                                                "curl fetch: --user-agent option requires a parameter",
211                                                TCL_STATIC);
212                                        theResult = TCL_ERROR;
213                                        break;                                 
214                                }
215                        } else {
216                                char theErrorString[512];
217                                (void) snprintf(theErrorString, sizeof(theErrorString),
218                                        "curl fetch: unknown option %s", theOption);
219                                Tcl_SetResult(interp, theErrorString, TCL_VOLATILE);
220                                theResult = TCL_ERROR;
221                                break;
222                        }
223                       
224                        optioncrsr++;
225                }
226               
227                if (optioncrsr <= lastoption) {
228                        /* something went wrong */
229                        break;
230                }
231
232                /*      first (second) parameter is -v or the url,
233                        second (third) parameter is the file */
234
235                if (objc >= 4) {
236                        /* Retrieve the url */
237                        theURL = Tcl_GetString(objv[objc - 2]);
238       
239                        /* Retrieve the file path */
240                        theFilePath = Tcl_GetString(objv[objc - 1]);
241                } else {
242                        Tcl_WrongNumArgs(interp, 1, objv, "fetch [options] url file");
243                        theResult = TCL_ERROR;
244                        break;
245                }
246               
247                /* Open the file */
248                theFile = fopen( theFilePath, "w" );
249                if (theFile == NULL) {
250                        Tcl_SetResult(interp, strerror(errno), TCL_VOLATILE);
251                        theResult = TCL_ERROR;
252                }
253
254                /* Create the CURL handle */
255                theHandle = curl_easy_init();
256               
257                /* Setup the handle */
258                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_URL, theURL);
259                if (theCurlCode != CURLE_OK) {
260                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
261                        break;
262                }
263               
264                /* -L option */
265                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_FOLLOWLOCATION, 1);
266                if (theCurlCode != CURLE_OK) {
267                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
268                        break;
269                }
270
271                /* -f option */
272                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_FAILONERROR, 1);
273                if (theCurlCode != CURLE_OK) {
274                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
275                        break;
276                }
277
278        /* -A option */
279        theCurlCode = curl_easy_setopt(theHandle, CURLOPT_USERAGENT, userAgent);
280        if (theCurlCode != CURLE_OK) {
281            theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
282            break;
283        }
284
285                /* skip the header data */
286                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_HEADER, 0);
287                if (theCurlCode != CURLE_OK) {
288                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
289                        break;
290                }
291               
292                /* write to the file */
293                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_WRITEDATA, theFile);
294                if (theCurlCode != CURLE_OK) {
295                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
296                        break;
297                }
298               
299                /* we want/don't want progress */
300                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_NOPROGRESS, noprogress);
301                if (theCurlCode != CURLE_OK) {
302                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
303                        break;
304                }
305
306                /* we want/don't want to use epsv */
307                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_FTP_USE_EPSV, useepsv);
308                if (theCurlCode != CURLE_OK) {
309                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
310                        break;
311                }
312               
313                /* we may want to ignore ssl errors */
314                if (ignoresslcert) {
315                        theCurlCode = curl_easy_setopt(theHandle, CURLOPT_SSL_VERIFYPEER, (long) 0);
316                        if (theCurlCode != CURLE_OK) {
317                                theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
318                                break;
319                        }
320                        theCurlCode = curl_easy_setopt(theHandle, CURLOPT_SSL_VERIFYHOST, (long) 0);
321                        if (theCurlCode != CURLE_OK) {
322                                theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
323                                break;
324                        }
325                }
326
327                /* we want/don't want remote time */
328                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_FILETIME, remotetime);
329                if (theCurlCode != CURLE_OK) {
330                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
331                        break;
332                }
333
334                /* set the l/p, if any */
335                if (theUserPassString) {
336                        theCurlCode = curl_easy_setopt(theHandle, CURLOPT_USERPWD, theUserPassString);
337                        if (theCurlCode != CURLE_OK) {
338                                theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
339                                break;
340                        }
341                }
342
343                /* Clear the Pragma: no-cache header */
344                headers = curl_slist_append(headers, "Pragma:");
345                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_HTTPHEADER, headers);
346                if (theCurlCode != CURLE_OK) {
347                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
348                        break;
349                }
350
351                /* actually fetch the resource */
352                theCurlCode = curl_easy_perform(theHandle);
353                if (theCurlCode != CURLE_OK) {
354                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
355                        break;
356                }
357               
358                /* close the file */
359                (void) fclose( theFile );
360                theFile = NULL;
361               
362#ifdef HAVE_UTIME_H
363                if (remotetime) {
364                        theCurlCode = curl_easy_getinfo(theHandle, CURLINFO_FILETIME, &theFileTime);
365                        if (theFileTime > 0) {
366                                struct utimbuf times;
367                                times.actime = (time_t)theFileTime;
368                                times.modtime = (time_t)theFileTime;
369                                utime(theFilePath, &times); /* set the time we got */
370                        }
371                }
372#endif /*HAVE_UTIME_H*/
373               
374                /* free header memory */
375                curl_slist_free_all(headers);
376
377                /* If --effective-url option was given, set given variable name to last effective url used by curl */
378                if (effectiveURLVarName != NULL) {
379                        theCurlCode = curl_easy_getinfo(theHandle, CURLINFO_EFFECTIVE_URL, &effectiveURL);
380                        Tcl_SetVar(interp, effectiveURLVarName,
381                                (effectiveURL == NULL || theCurlCode != CURLE_OK) ? "" : effectiveURL,
382                                0);
383                }
384               
385                /* check everything went fine */
386                theCurlCode = curl_easy_getinfo(theHandle, CURLINFO_HTTP_CODE, &theResponseCode);
387                if (theCurlCode != CURLE_OK) {
388                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
389                        break;
390                }
391               
392                /* we need something between 200 (incl.) and 300 (excl.).*/
393                /* (actually, we sometimes get 0 from GNU FTP servers) */
394                if (((theResponseCode != 0)  && (theResponseCode < 200))
395                        || (theResponseCode >= 300)) {
396                        char theErrorString[512];
397                        (void) snprintf(theErrorString, sizeof(theErrorString),
398                                "Download failed (code = %li)", theResponseCode);
399                        Tcl_SetResult(interp, theErrorString, TCL_VOLATILE);
400                        theResult = TCL_ERROR;
401                        break;
402                }
403               
404                /* clean up */
405                curl_easy_cleanup( theHandle );
406                theHandle = NULL;
407    } while (0);
408   
409    if (theHandle != NULL) {
410        curl_easy_cleanup( theHandle );
411    }
412    if (theFile != NULL) {
413        fclose( theFile );
414    }
415   
416        return theResult;
417}
418
419/**
420 * curl isnewer subcommand entry point.
421 *
422 * @param interp                current interpreter
423 * @param objc                  number of parameters
424 * @param objv                  parameters
425 */
426int
427CurlIsNewerCmd(Tcl_Interp* interp, int objc, Tcl_Obj* CONST objv[])
428{
429        int theResult = TCL_OK;
430        CURL* theHandle = NULL;
431        FILE* theFile = NULL;
432
433        do {
434                long theResponseCode = 0;
435                const char* theURL;
436                CURLcode theCurlCode;
437                long theModDate;
438                long userModDate;
439                               
440                /*      first (second) parameter is the url,
441                        second (third) parameter is the date */
442                if (objc != 4) {
443                        Tcl_WrongNumArgs(interp, 1, objv, "isnewer url date");
444                        theResult = TCL_ERROR;
445                        break;
446                }
447
448                /* Retrieve the url */
449                theURL = Tcl_GetString(objv[2]);
450
451                /* Get the date */
452                theResult = Tcl_GetLongFromObj(interp, objv[3], &userModDate);
453                if (theResult != TCL_OK) {
454                        break;
455                }
456               
457                /* Open the file (dev/null) */
458                theFile = fopen( "/dev/null", "a" );
459                if (theFile == NULL) {
460                        Tcl_SetResult(interp, strerror(errno), TCL_VOLATILE);
461                        theResult = TCL_ERROR;
462                }
463
464                /* Create the CURL handle */
465                theHandle = curl_easy_init();
466               
467                /* Setup the handle */
468                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_URL, theURL);
469                if (theCurlCode != CURLE_OK) {
470                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
471                        break;
472                }
473               
474                /* -L option */
475                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_FOLLOWLOCATION, 1);
476                if (theCurlCode != CURLE_OK) {
477                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
478                        break;
479                }
480
481                /* -f option */
482                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_FAILONERROR, 1);
483                if (theCurlCode != CURLE_OK) {
484                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
485                        break;
486                }
487
488                /* set timeout on connections */
489                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_TIMEOUT, _CURL_CONNECTION_TIMEOUT);
490                if (theCurlCode != CURLE_OK) {
491                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
492                        break;
493                }
494
495                /* set minimum connection speed */
496                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_LOW_SPEED_LIMIT, _CURL_MINIMUM_XFER_SPEED);
497                if (theCurlCode != CURLE_OK) {
498                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
499                        break;
500                }
501
502                /* set timeout interval for connections < min xfer speed */
503                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_LOW_SPEED_TIME, _CURL_MINIMUM_XFER_TIMEOUT);
504                if (theCurlCode != CURLE_OK) {
505                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
506                        break;
507                }
508
509                /* write to the file */
510                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_WRITEDATA, theFile);
511                if (theCurlCode != CURLE_OK) {
512                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
513                        break;
514                }
515               
516                /* save the modification date */
517                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_FILETIME, 1);
518                if (theCurlCode != CURLE_OK) {
519                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
520                        break;
521                }
522
523                /* skip the download if the file wasn't modified */
524                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE);
525                if (theCurlCode != CURLE_OK) {
526                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
527                        break;
528                }
529                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_TIMEVALUE, userModDate);
530                if (theCurlCode != CURLE_OK) {
531                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
532                        break;
533                }
534
535                /* we do not want any progress */
536                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_NOPROGRESS, 1);
537                if (theCurlCode != CURLE_OK) {
538                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
539                        break;
540                }
541               
542                /* actually fetch the resource */
543                theCurlCode = curl_easy_perform(theHandle);
544                if (theCurlCode != CURLE_OK) {
545                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
546                        break;
547                }
548               
549                /* close the file */
550                (void) fclose( theFile );
551                theFile = NULL;
552               
553                /* check everything went fine */
554                theCurlCode = curl_easy_getinfo(theHandle, CURLINFO_HTTP_CODE, &theResponseCode);
555                if (theCurlCode != CURLE_OK) {
556                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
557                        break;
558                }
559
560                theModDate = -1;
561
562                if (theResponseCode != 304) {
563                        /* get the modification date */
564                        theCurlCode = curl_easy_getinfo(theHandle, CURLINFO_FILETIME, &theModDate);
565                        if (theCurlCode != CURLE_OK) {
566                                theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
567                                break;
568                        }
569       
570                        /* clean up */
571                        curl_easy_cleanup( theHandle );
572                        theHandle = NULL;
573       
574                        /* compare this with the date provided by user */
575                        if (theModDate < -1) {
576                                Tcl_SetResult(interp, "Couldn't get resource modification date", TCL_STATIC);
577                                theResult = TCL_ERROR;
578                                break;
579                        }
580                }
581
582                if (theModDate > userModDate) {
583                        Tcl_SetResult(interp, "1", TCL_STATIC);
584                } else {
585                        Tcl_SetResult(interp, "0", TCL_STATIC);
586                }               
587    } while (0);
588   
589    if (theHandle != NULL) {
590        curl_easy_cleanup( theHandle );
591    }
592    if (theFile != NULL) {
593        fclose( theFile );
594    }
595   
596        return theResult;
597}
598
599/**
600 * curl getsize subcommand entry point.
601 *
602 * @param interp                current interpreter
603 * @param objc                  number of parameters
604 * @param objv                  parameters
605 */
606int
607CurlGetSizeCmd(Tcl_Interp* interp, int objc, Tcl_Obj* CONST objv[])
608{
609        int theResult = TCL_OK;
610        CURL* theHandle = NULL;
611        FILE* theFile = NULL;
612
613        do {
614                char theSizeString[32];
615                const char* theURL;
616                CURLcode theCurlCode;
617                double theFileSize;
618                               
619                /*      first (second) parameter is the url */
620                if (objc != 3) {
621                        Tcl_WrongNumArgs(interp, 1, objv, "getsize url");
622                        theResult = TCL_ERROR;
623                        break;
624                }
625
626                /* Retrieve the url */
627                theURL = Tcl_GetString(objv[2]);
628
629                /* Open the file (dev/null) */
630                theFile = fopen( "/dev/null", "a" );
631                if (theFile == NULL) {
632                        Tcl_SetResult(interp, strerror(errno), TCL_VOLATILE);
633                        theResult = TCL_ERROR;
634                }
635
636                /* Create the CURL handle */
637                theHandle = curl_easy_init();
638               
639                /* Setup the handle */
640                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_URL, theURL);
641                if (theCurlCode != CURLE_OK) {
642                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
643                        break;
644                }
645               
646                /* -L option */
647                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_FOLLOWLOCATION, 1);
648                if (theCurlCode != CURLE_OK) {
649                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
650                        break;
651                }
652
653                /* -f option */
654                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_FAILONERROR, 1);
655                if (theCurlCode != CURLE_OK) {
656                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
657                        break;
658                }
659
660                /* set timeout on connections */
661                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_TIMEOUT, _CURL_CONNECTION_TIMEOUT);
662                if (theCurlCode != CURLE_OK) {
663                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
664                        break;
665                }
666
667                /* set minimum connection speed */
668                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_LOW_SPEED_LIMIT, _CURL_MINIMUM_XFER_SPEED);
669                if (theCurlCode != CURLE_OK) {
670                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
671                        break;
672                }
673
674                /* set timeout interval for connections < min xfer speed */
675                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_LOW_SPEED_TIME, _CURL_MINIMUM_XFER_TIMEOUT);
676                if (theCurlCode != CURLE_OK) {
677                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
678                        break;
679                }
680
681                /* write to the file */
682                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_WRITEDATA, theFile);
683                if (theCurlCode != CURLE_OK) {
684                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
685                        break;
686                }
687
688                /* skip the header data */
689                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_HEADER, 0);
690                if (theCurlCode != CURLE_OK) {
691                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
692                        break;
693                }
694               
695                /* skip the body data */
696                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_NOBODY, 1);
697                if (theCurlCode != CURLE_OK) {
698                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
699                        break;
700                }
701
702                /* we do not want any progress */
703                theCurlCode = curl_easy_setopt(theHandle, CURLOPT_NOPROGRESS, 1);
704                if (theCurlCode != CURLE_OK) {
705                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
706                        break;
707                }
708               
709                /* actually fetch the resource */
710                theCurlCode = curl_easy_perform(theHandle);
711                if (theCurlCode != CURLE_OK) {
712                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
713                        break;
714                }
715               
716                /* close the file */
717                (void) fclose( theFile );
718                theFile = NULL;
719
720                theFileSize = 0.0;
721
722                /* get the file size */
723                theCurlCode = curl_easy_getinfo(theHandle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &theFileSize);
724                if (theCurlCode != CURLE_OK) {
725                        theResult = SetResultFromCurlErrorCode(interp, theCurlCode);
726                        break;
727                }
728       
729                /* clean up */
730                curl_easy_cleanup( theHandle );
731                theHandle = NULL;
732
733                (void) snprintf(theSizeString, sizeof(theSizeString),
734                        "%.0f", theFileSize);
735                Tcl_SetResult(interp, theSizeString, TCL_VOLATILE);
736    } while (0);
737   
738    if (theHandle != NULL) {
739        curl_easy_cleanup( theHandle );
740    }
741    if (theFile != NULL) {
742        fclose( theFile );
743    }
744   
745        return theResult;
746}
747
748/**
749 * curl command entry point.
750 *
751 * @param clientData    custom data (ignored)
752 * @param interp                current interpreter
753 * @param objc                  number of parameters
754 * @param objv                  parameters
755 */
756int
757CurlCmd(
758                ClientData clientData UNUSED,
759                Tcl_Interp* interp,
760                int objc, 
761                Tcl_Obj* CONST objv[])
762{
763    typedef enum {
764        kCurlFetch,
765        kCurlIsNewer,
766        kCurlGetSize
767    } EOption;
768   
769        static tableEntryString options[] = {
770                "fetch", "isnewer", "getsize", NULL
771        };
772        int theResult = TCL_OK;
773    EOption theOptionIndex;
774
775        if (objc < 3) {
776                Tcl_WrongNumArgs(interp, 1, objv, "option ?arg ...?");
777                return TCL_ERROR;
778        }
779
780        theResult = Tcl_GetIndexFromObj(
781                                interp,
782                                objv[1],
783                                options,
784                                "option",
785                                0,
786                                (int*) &theOptionIndex);
787        if (theResult == TCL_OK) {
788                switch (theOptionIndex)
789                {
790                        case kCurlFetch:
791                                theResult = CurlFetchCmd(interp, objc, objv);
792                                break;
793
794                        case kCurlIsNewer:
795                                theResult = CurlIsNewerCmd(interp, objc, objv);
796                                break;
797
798                        case kCurlGetSize:
799                                theResult = CurlGetSizeCmd(interp, objc, objv);
800                                break;
801                }
802        }
803       
804        return theResult;
805}
806
807/**
808 * curl init entry point.
809 *
810 * @param interp                current interpreter
811 */
812int
813CurlInit(Tcl_Interp* interp)
814{
815        CURLcode theCurlCode = curl_global_init(CURL_GLOBAL_ALL);
816        return SetResultFromCurlErrorCode(interp, theCurlCode);
817}
818
819/* ============================================================== **
820** As of next Thursday, UNIX will be flushed in favor of TOPS-10. **
821** Please update your programs.                                   **
822** ============================================================== */
Note: See TracBrowser for help on using the repository browser.