Annotation of libwww/Library/src/HTFWrite.c, revision 2.26
2.1 frystyk 1: /* HTFWrite.c
2: ** FILE WRITER
3: **
2.6 frystyk 4: ** (c) COPYRIGHT MIT 1995.
2.1 frystyk 5: ** Please first read the full copyright statement in the file COPYRIGH.
6: **
7: ** This version of the stream object just writes to a C file.
8: ** The file is assumed open and left open.
9: **
10: ** Bugs:
11: ** strings written must be less than buffer size.
12: **
13: ** History:
14: ** HFN: wrote it
15: ** HWL: converted the caching scheme to be hierachical by taking
16: ** AL code from Deamon
2.10 frystyk 17: ** HFN: moved cache code to HTCache module
2.1 frystyk 18: **
19: */
20:
21: /* Library include files */
22: #include "tcp.h"
23: #include "HTUtils.h"
24: #include "HTString.h"
25: #include "HTFormat.h"
2.21 frystyk 26: #include "HTError.h"
2.1 frystyk 27: #include "HTAlert.h"
2.19 frystyk 28: #include "HTAccess.h"
2.4 frystyk 29: #include "HTBind.h"
2.1 frystyk 30: #include "HTList.h"
2.3 frystyk 31: #include "HTParse.h"
2.15 frystyk 32: #include "HTReq.h"
2.3 frystyk 33: #include "HTFWrite.h" /* Implemented here */
2.1 frystyk 34:
2.4 frystyk 35: #define HASH_SIZE 1001 /* Tunable */
36:
2.13 frystyk 37: /* The default directory for "save locally" and "save and execute" files: */
38: #ifndef HT_TMP_ROOT
39: #define HT_TMP_ROOT "/tmp"
40: #endif
41:
2.3 frystyk 42: struct _HTStream {
2.24 frystyk 43: CONST HTStreamClass *isa;
44:
45: FILE * fp;
46: BOOL leave_open; /* Close file when free? */
47: char * end_command; /* Command to execute */
48: BOOL remove_on_close; /* Remove file? */
49: char * filename; /* Name of file */
50: HTRequest * request; /* saved for callback */
51: HTRequestCallback * callback;
2.3 frystyk 52: };
2.1 frystyk 53:
2.24 frystyk 54: PRIVATE HTStream HTBaseStreamInstance; /* Made static */
55:
2.3 frystyk 56: PRIVATE char * HTTmpRoot = NULL; /* Dest for tmp files */
2.1 frystyk 57:
2.3 frystyk 58: /* ------------------------------------------------------------------------- */
59: /* BASIC STREAM CLASSES */
60: /* ------------------------------------------------------------------------- */
2.1 frystyk 61:
2.3 frystyk 62: /*
2.1 frystyk 63: **
2.3 frystyk 64: ** B L A C K H O L E C L A S S
2.1 frystyk 65: **
2.3 frystyk 66: ** There is only one black hole instance shared by anyone
67: ** who wants a black hole. These black holes don't radiate,
68: ** they just absorb data.
69: */
2.16 frystyk 70: PRIVATE int HTBlackHole_put_character (HTStream * me, char c)
2.3 frystyk 71: {
72: return HT_OK;
73: }
74:
2.16 frystyk 75: PRIVATE int HTBlackHole_put_string (HTStream * me, CONST char * s)
2.3 frystyk 76: {
77: return HT_OK;
78: }
79:
2.16 frystyk 80: PRIVATE int HTBlackHole_write (HTStream * me, CONST char * s, int l)
2.3 frystyk 81: {
82: return HT_OK;
83: }
84:
2.16 frystyk 85: PRIVATE int HTBlackHole_flush (HTStream * me)
2.3 frystyk 86: {
87: return HT_OK;
88: }
89:
2.16 frystyk 90: PRIVATE int HTBlackHole_free (HTStream * me)
2.3 frystyk 91: {
92: return HT_OK;
93: }
94:
2.18 frystyk 95: PRIVATE int HTBlackHole_abort (HTStream * me, HTList * e)
2.3 frystyk 96: {
97: return HT_ERROR;
98: }
99:
100:
101: /* Black Hole stream
102: ** -----------------
103: */
104: PRIVATE CONST HTStreamClass HTBlackHoleClass =
105: {
106: "BlackHole",
107: HTBlackHole_flush,
108: HTBlackHole_free,
109: HTBlackHole_abort,
110: HTBlackHole_put_character,
111: HTBlackHole_put_string,
112: HTBlackHole_write
113: };
114:
2.16 frystyk 115: PUBLIC HTStream * HTBlackHole (void)
2.3 frystyk 116: {
2.24 frystyk 117: if (STREAM_TRACE) TTYPrint(TDEST, "BlackHole... Created\n");
118: HTBaseStreamInstance.isa = &HTBlackHoleClass; /* The rest is random */
119: return &HTBaseStreamInstance;
2.3 frystyk 120: }
121:
2.23 frystyk 122: PUBLIC HTStream * HTBlackHoleConverter (HTRequest * request,
123: void * param,
124: HTFormat input_format,
125: HTFormat output_format,
126: HTStream * output_stream)
127: {
128: return HTBlackHole();
129: }
2.3 frystyk 130:
2.24 frystyk 131: /*
132: ** ERROR STREAM
133: ** ------------
134: ** There is only one error stream shared by anyone who wants a
135: ** generic error returned from all stream methods.
136: */
137: PRIVATE int HTErrorStream_put_character (HTStream * me, char c)
138: {
139: return HT_ERROR;
140: }
141:
142: PRIVATE int HTErrorStream_put_string (HTStream * me, CONST char * s)
143: {
144: return HT_ERROR;
145: }
146:
147: PRIVATE int HTErrorStream_write (HTStream * me, CONST char * s, int l)
148: {
149: return HT_ERROR;
150: }
151:
152: PRIVATE int HTErrorStream_flush (HTStream * me)
153: {
154: return HT_ERROR;
155: }
156:
157: PRIVATE int HTErrorStream_free (HTStream * me)
158: {
159: return HT_ERROR;
160: }
161:
162: PRIVATE int HTErrorStream_abort (HTStream * me, HTList * e)
163: {
164: return HT_ERROR;
165: }
166:
167: PRIVATE CONST HTStreamClass HTErrorStreamClass =
168: {
169: "ErrorStream",
170: HTErrorStream_flush,
171: HTErrorStream_free,
172: HTErrorStream_abort,
173: HTErrorStream_put_character,
174: HTErrorStream_put_string,
175: HTErrorStream_write
176: };
177:
178: PUBLIC HTStream * HTErrorStream (void)
179: {
180: if (STREAM_TRACE) TTYPrint(TDEST, "ErrorStream. Created\n");
181: HTBaseStreamInstance.isa = &HTErrorStreamClass; /* The rest is random */
182: return &HTBaseStreamInstance;
183: }
184:
2.3 frystyk 185: /* HTThroughLine
186: ** -------------
2.1 frystyk 187: **
2.3 frystyk 188: ** This function is a dummy function that returns the same output stream
189: ** as given as a parameter. Henrik 01/03-94
2.1 frystyk 190: */
2.16 frystyk 191: PUBLIC HTStream* HTThroughLine (HTRequest * request,
192: void * param,
193: HTFormat input_format,
194: HTFormat output_format,
195: HTStream * output_stream)
2.3 frystyk 196: {
197: return output_stream;
198: }
2.1 frystyk 199:
2.3 frystyk 200: /* ------------------------------------------------------------------------- */
201: /* SOCKET WRITER STREAM */
202: /* ------------------------------------------------------------------------- */
203:
2.16 frystyk 204: PRIVATE int HTFWriter_put_character (HTStream * me, char c)
2.3 frystyk 205: {
206: return (fputc(c, me->fp) == EOF) ? HT_ERROR : HT_OK;
207: }
208:
2.16 frystyk 209: PRIVATE int HTFWriter_put_string (HTStream * me, CONST char* s)
2.3 frystyk 210: {
211: if (*s) /* For vms :-( 10/04-94 */
212: return (fputs(s, me->fp) == EOF) ? HT_ERROR : HT_OK;
213: return HT_OK;
214: }
215:
2.16 frystyk 216: PRIVATE int HTFWriter_flush (HTStream * me)
2.3 frystyk 217: {
2.7 frystyk 218: return (fflush(me->fp) == EOF) ? HT_ERROR : HT_OK;
2.3 frystyk 219: }
220:
2.16 frystyk 221: PRIVATE int HTFWriter_write (HTStream * me, CONST char* s, int l)
2.3 frystyk 222: {
2.7 frystyk 223: int status ;
224: status = (fwrite(s, 1, l, me->fp) != l) ? HT_ERROR : HT_OK ;
2.24 frystyk 225: if (l > 1 && status == HT_OK) (void) HTFWriter_flush(me);
2.7 frystyk 226: return status ;
2.3 frystyk 227: }
2.7 frystyk 228:
2.16 frystyk 229: PRIVATE int HTFWriter_free (HTStream * me)
2.3 frystyk 230: {
2.24 frystyk 231: if (me) {
232: if (me->leave_open != YES) fclose(me->fp);
2.17 frystyk 233: #ifdef GOT_SYSTEM
2.24 frystyk 234: if (me->end_command) system(me->end_command); /* SECURITY HOLE!!! */
2.17 frystyk 235: #endif
2.24 frystyk 236: if (me->remove_on_close) REMOVE(me->filename);
237: if (me->callback) (*me->callback)(me->request, me->filename);
2.26 ! frystyk 238: HT_FREE(me->end_command);
! 239: HT_FREE(me->filename);
! 240: HT_FREE(me);
2.3 frystyk 241: }
242: return HT_OK;
243: }
244:
2.18 frystyk 245: PRIVATE int HTFWriter_abort (HTStream * me, HTList * e)
2.3 frystyk 246: {
2.24 frystyk 247: if (STREAM_TRACE) TTYPrint(TDEST, "FileWriter.. ABORTING...\n");
248: if (me) {
249: if (me->leave_open != YES) fclose(me->fp);
250: if (me->remove_on_close) REMOVE(me->filename);
2.26 ! frystyk 251: HT_FREE(me->end_command);
! 252: HT_FREE(me->filename);
! 253: HT_FREE(me);
2.3 frystyk 254: }
255: return HT_ERROR;
256: }
257:
258: PRIVATE CONST HTStreamClass HTFWriter = /* As opposed to print etc */
259: {
260: "FileWriter",
261: HTFWriter_flush,
262: HTFWriter_free,
263: HTFWriter_abort,
264: HTFWriter_put_character,
265: HTFWriter_put_string,
266: HTFWriter_write
267: };
268:
2.24 frystyk 269: PUBLIC HTStream * HTFWriter_new (HTRequest * request, FILE * fp,
270: BOOL leave_open)
2.3 frystyk 271: {
2.24 frystyk 272: HTStream * me = NULL;
2.3 frystyk 273: if (!fp) {
2.24 frystyk 274: if (STREAM_TRACE)TTYPrint(TDEST, "FileWriter.. Bad file descriptor\n");
275: return HTErrorStream();
2.3 frystyk 276: }
2.26 ! frystyk 277: if ((me = (HTStream *) HT_CALLOC(1, sizeof(HTStream))) == NULL)
! 278: HT_OUTOFMEM("HTFWriter_new");
2.3 frystyk 279: me->isa = &HTFWriter;
280: me->fp = fp;
2.24 frystyk 281: me->leave_open = leave_open;
282: me->request = request;
2.3 frystyk 283: return me;
284: }
285:
286: /* ------------------------------------------------------------------------- */
287: /* FILE WRITER ROUTINES */
288: /* ------------------------------------------------------------------------- */
289:
290: /* Set TMP Root
291: ** --------------
292: ** If `tmp_root' is NULL use the current value (might be a define)
293: */
2.16 frystyk 294: PUBLIC BOOL HTTmp_setRoot (CONST char * tmp_root)
2.3 frystyk 295: {
296: StrAllocCopy(HTTmpRoot, tmp_root ? tmp_root : HT_TMP_ROOT);
297: if (*(HTTmpRoot+strlen(HTTmpRoot)-1) != '/')
298: StrAllocCat(HTTmpRoot, "/");
2.14 frystyk 299: if (STREAM_TRACE)
2.17 frystyk 300: TTYPrint(TDEST, "Tmp Root.... Root set to `%s\'\n", HTTmpRoot);
2.3 frystyk 301: return YES;
302: }
303:
304:
305: /* Get Tmp Root
306: ** --------------
2.1 frystyk 307: */
2.16 frystyk 308: PUBLIC CONST char * HTTmp_getRoot (void)
2.3 frystyk 309: {
310: return HTTmpRoot;
311: }
2.1 frystyk 312:
313:
2.3 frystyk 314: /* Free Tmp Root
315: ** --------------
316: ** For clean up memory
317: */
2.16 frystyk 318: PUBLIC void HTTmp_freeRoot (void)
2.3 frystyk 319: {
2.26 ! frystyk 320: HT_FREE(HTTmpRoot);
2.3 frystyk 321: }
2.1 frystyk 322:
2.4 frystyk 323: /*
324: ** This function tries really hard to find a non-existent filename relative
325: ** to the path given. Returns a string that must be freed by the caller or
326: ** NULL on error. The base must be '/' terminated which!
327: */
2.16 frystyk 328: PRIVATE char *get_filename (char * base, CONST char * url, CONST char * suffix)
2.4 frystyk 329: {
330: char *path=NULL;
331: char filename[40];
332: int hash=0;
333:
334: /* Do this until we find a name that doesn't exist */
335: while (1)
336: {
337: CONST char *ptr=url;
338: for( ; *ptr; ptr++)
339: hash = (int) ((hash * 31 + (* (unsigned char *) ptr)) % HASH_SIZE);
340:
341: #ifndef NO_GETPID
342: sprintf(filename, "%d-%d", hash, (int) getpid());
343: #else
344: sprintf(filename, "%d-%d", hash, time(NULL));
345: #endif
346: StrAllocCopy(path, base);
347: StrAllocCat(path, filename);
348: if (suffix) StrAllocCat(path, suffix);
349:
350: {
351: FILE *fp = fopen(path, "r");
352: if (fp) /* This file does already exist */
353: fclose(fp);
354: else
355: break; /* Got the file name */
356: }
357: }
358: return path;
359: }
360:
361:
2.24 frystyk 362: /* Save Locally
363: ** ------------
364: ** Saves a file to local disk. This can for example be used to dump
365: ** date objects of unknown media types to local disk. The stream prompts
366: ** for a file name for the temporary file.
2.1 frystyk 367: */
2.24 frystyk 368: PUBLIC HTStream* HTSaveLocally (HTRequest * request,
369: void * param,
370: HTFormat input_format,
371: HTFormat output_format,
372: HTStream * output_stream)
2.1 frystyk 373: {
2.24 frystyk 374: FILE * fp = NULL;
375: char * filename = NULL;
2.19 frystyk 376: if (HTLib_secure()) {
2.21 frystyk 377: HTRequest_addError(request, ERR_NON_FATAL, NO, HTERR_UNAUTHORIZED,
2.24 frystyk 378: NULL, 0, "HTSaveLocally");
379: return HTErrorStream();
2.1 frystyk 380: }
2.3 frystyk 381: if (!HTTmpRoot) {
2.24 frystyk 382: if (STREAM_TRACE) TTYPrint(TDEST, "Save File... turned off");
383: return HTErrorStream();
2.1 frystyk 384: }
385:
2.24 frystyk 386: /* Let's prompt the user for a file name for this file */
2.4 frystyk 387: {
2.24 frystyk 388: HTAlertCallback *cbf = HTAlert_find(HT_A_PROMPT);
2.13 frystyk 389: HTParentAnchor *anchor = (HTParentAnchor *) HTRequest_anchor(request);
390: char *suffix = HTBind_getSuffix(anchor);
2.24 frystyk 391: char *deflt = get_filename(HTTmpRoot,HTAnchor_physical(anchor),suffix);
392: if (cbf) {
393: HTAlertPar * reply = HTAlert_newReply();
394: if ((*cbf)(request, HT_A_PROMPT, HT_MSG_FILENAME,deflt,NULL,reply))
395: filename = HTAlert_replyMessage(reply);
396: HTAlert_deleteReply(reply);
397: }
2.26 ! frystyk 398: HT_FREE(suffix);
! 399: HT_FREE(deflt);
2.24 frystyk 400: if (filename) {
401: if ((fp = fopen(filename, "wb")) == NULL) {
402: HTRequest_addError(request, ERR_NON_FATAL, NO, HTERR_NO_FILE,
403: filename, strlen(filename),"HTSaveLocally");
2.26 ! frystyk 404: HT_FREE(filename);
2.24 frystyk 405: return HTErrorStream();
406: }
407: } else {
408: if (STREAM_TRACE) TTYPrint(TDEST, "Save File... No file name\n");
409: return HTErrorStream();
2.4 frystyk 410: }
411: }
2.24 frystyk 412:
413: /* Now we are ready for creating the file writer stream */
414: if (fp) {
415: HTStream * me = HTFWriter_new(request, fp, NO);
416: me->filename = filename;
417: return me;
2.1 frystyk 418: }
2.26 ! frystyk 419: HT_FREE(filename);
2.24 frystyk 420: return HTErrorStream();
2.1 frystyk 421: }
422:
423:
2.24 frystyk 424: /* Take action using a system command
425: ** ----------------------------------
426: ** Creates temporary file, writes to it and then executes system
427: ** command (maybe an external viewer) when EOF has been reached. The
428: ** stream finds a suitable name of the temporary file which preserves the
429: ** suffix. This way, the system command can find out the file type from
430: ** the name of the temporary file name.
2.1 frystyk 431: */
2.24 frystyk 432: PUBLIC HTStream* HTSaveAndExecute (HTRequest * request,
433: void * param,
434: HTFormat input_format,
435: HTFormat output_format,
436: HTStream * output_stream)
2.1 frystyk 437: {
2.24 frystyk 438: FILE * fp = NULL;
439: char * filename = NULL;
2.19 frystyk 440: if (HTLib_secure()) {
2.21 frystyk 441: HTRequest_addError(request, ERR_NON_FATAL, NO, HTERR_UNAUTHORIZED,
442: NULL, 0, "HTSaveLocally");
2.24 frystyk 443: return HTErrorStream();
2.1 frystyk 444: }
2.3 frystyk 445: if (!HTTmpRoot) {
2.24 frystyk 446: if (STREAM_TRACE) TTYPrint(TDEST, "Save File... turned off");
447: return HTErrorStream();
2.1 frystyk 448: }
449:
2.24 frystyk 450: /* Let's find a hash name for this file without asking user */
2.4 frystyk 451: {
2.13 frystyk 452: HTParentAnchor *anchor = (HTParentAnchor *) HTRequest_anchor(request);
453: char *suffix = HTBind_getSuffix(anchor);
2.24 frystyk 454: filename = get_filename(HTTmpRoot, HTAnchor_physical(anchor), suffix);
2.26 ! frystyk 455: HT_FREE(suffix);
2.25 frystyk 456: if (filename) {
457: if ((fp = fopen(filename, "wb")) == NULL) {
458: HTRequest_addError(request, ERR_NON_FATAL, NO, HTERR_NO_FILE,
459: filename, strlen(filename),"HTSaveAndExecute");
2.26 ! frystyk 460: HT_FREE(filename);
2.25 frystyk 461: return HTErrorStream();
462: }
463: } else {
464: if (STREAM_TRACE) TTYPrint(TDEST, "Save File... No file name\n");
2.24 frystyk 465: return HTErrorStream();
2.4 frystyk 466: }
467: }
468:
2.24 frystyk 469: /* Now we are ready for creating the file writer stream */
470: if (fp) {
471: HTStream * me = HTFWriter_new(request, fp, NO);
472: me->filename = filename;
2.26 ! frystyk 473: if ((me->end_command = (char *) HT_MALLOC((strlen((char *) param) + 10 + 3*strlen(filename)))) == NULL)
! 474: HT_OUTOFMEM("SaveAndExecute");
2.24 frystyk 475: sprintf (me->end_command, (char *)param, filename, filename, filename);
476: return me;
2.1 frystyk 477: }
2.26 ! frystyk 478: HT_FREE(filename);
2.24 frystyk 479: return HTErrorStream();
2.1 frystyk 480: }
481:
482:
483: /* Save and Call Back
484: ** ------------------
2.24 frystyk 485: ** This stream works exactly like the TSaveAndExecute
486: ** stream but in addition when EOF has been reached, it checks whether a
487: ** callback function has been associated with the request object in which
488: ** case, this callback is being called. This can be use by the
489: ** application to do some processing after the system command
490: ** has terminated. The callback function is called with the file name of
491: ** the temporary file as parameter.
2.1 frystyk 492: */
2.16 frystyk 493: PUBLIC HTStream* HTSaveAndCallback (HTRequest * request,
494: void * param,
495: HTFormat input_format,
496: HTFormat output_format,
497: HTStream * output_stream)
2.1 frystyk 498: {
2.24 frystyk 499: HTStream * me = HTSaveAndExecute(request, param, input_format,
500: output_format, output_stream);
501: if (me) {
502: me->callback = HTRequest_callback(request);
503: return me;
504: }
505: return HTErrorStream();
2.1 frystyk 506: }
507:
Webmaster