Annotation of libwww/Library/src/HTFWrite.c, revision 2.13
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"
26: #include "HTAlert.h"
2.4 frystyk 27: #include "HTBind.h"
2.1 frystyk 28: #include "HTList.h"
2.3 frystyk 29: #include "HTParse.h"
30: #include "HTFWrite.h" /* Implemented here */
2.1 frystyk 31:
2.4 frystyk 32: #define HASH_SIZE 1001 /* Tunable */
33:
2.13 ! frystyk 34: /* The default directory for "save locally" and "save and execute" files: */
! 35: #ifndef HT_TMP_ROOT
! 36: #define HT_TMP_ROOT "/tmp"
! 37: #endif
! 38:
2.3 frystyk 39: struct _HTStream {
40: CONST HTStreamClass * isa;
41:
42: FILE * fp;
43: BOOL leave_open; /* Close file? HFN 08/02-94 */
44: char * end_command;
45: BOOL remove_on_close;
46: BOOL announce;
47: char * filename;
48: HTRequest * request; /* saved for callback */
49: BOOL (*callback) PARAMS((struct _HTRequest * req, void * filename));
50: };
2.1 frystyk 51:
2.3 frystyk 52: PRIVATE HTStream HTBlackHoleInstance; /* Made static */
53: PRIVATE char * HTTmpRoot = NULL; /* Dest for tmp files */
2.1 frystyk 54:
2.3 frystyk 55: /* ------------------------------------------------------------------------- */
56: /* BASIC STREAM CLASSES */
57: /* ------------------------------------------------------------------------- */
2.1 frystyk 58:
2.3 frystyk 59: /*
2.1 frystyk 60: **
2.3 frystyk 61: ** B L A C K H O L E C L A S S
2.1 frystyk 62: **
2.3 frystyk 63: ** There is only one black hole instance shared by anyone
64: ** who wants a black hole. These black holes don't radiate,
65: ** they just absorb data.
66: */
67: PRIVATE int HTBlackHole_put_character ARGS2(HTStream *, me, char, c)
68: {
69: return HT_OK;
70: }
71:
72: PRIVATE int HTBlackHole_put_string ARGS2(HTStream *, me, CONST char*, s)
73: {
74: return HT_OK;
75: }
76:
77: PRIVATE int HTBlackHole_write ARGS3(HTStream *, me, CONST char*, s, int, l)
78: {
79: return HT_OK;
80: }
81:
82: PRIVATE int HTBlackHole_flush ARGS1(HTStream *, me)
83: {
84: return HT_OK;
85: }
86:
87: PRIVATE int HTBlackHole_free ARGS1(HTStream *, me)
88: {
89: return HT_OK;
90: }
91:
92: PRIVATE int HTBlackHole_abort ARGS2(HTStream *, me, HTError, e)
93: {
94: return HT_ERROR;
95: }
96:
97:
98: /* Black Hole stream
99: ** -----------------
100: */
101: PRIVATE CONST HTStreamClass HTBlackHoleClass =
102: {
103: "BlackHole",
104: HTBlackHole_flush,
105: HTBlackHole_free,
106: HTBlackHole_abort,
107: HTBlackHole_put_character,
108: HTBlackHole_put_string,
109: HTBlackHole_write
110: };
111:
112: PUBLIC HTStream * HTBlackHole NOARGS
113: {
114: if (TRACE)
115: fprintf(TDEST, "BlackHole... Created\n");
116: HTBlackHoleInstance.isa = &HTBlackHoleClass; /* The rest is random */
117: return &HTBlackHoleInstance;
118: }
119:
120:
121: /* HTThroughLine
122: ** -------------
2.1 frystyk 123: **
2.3 frystyk 124: ** This function is a dummy function that returns the same output stream
125: ** as given as a parameter. Henrik 01/03-94
2.1 frystyk 126: */
2.3 frystyk 127: PUBLIC HTStream* HTThroughLine ARGS5(
128: HTRequest *, request,
129: void *, param,
130: HTFormat, input_format,
131: HTFormat, output_format,
132: HTStream *, output_stream) /* Only one used */
133: {
134: return output_stream;
135: }
2.1 frystyk 136:
2.3 frystyk 137: /* ------------------------------------------------------------------------- */
138: /* SOCKET WRITER STREAM */
139: /* ------------------------------------------------------------------------- */
140:
141: PRIVATE int HTFWriter_put_character ARGS2(HTStream *, me, char, c)
142: {
143: return (fputc(c, me->fp) == EOF) ? HT_ERROR : HT_OK;
144: }
145:
146: PRIVATE int HTFWriter_put_string ARGS2(HTStream *, me, CONST char*, s)
147: {
148: if (*s) /* For vms :-( 10/04-94 */
149: return (fputs(s, me->fp) == EOF) ? HT_ERROR : HT_OK;
150: return HT_OK;
151: }
152:
2.7 frystyk 153: PRIVATE int HTFWriter_flush ARGS1(HTStream *, me)
2.3 frystyk 154: {
2.7 frystyk 155: return (fflush(me->fp) == EOF) ? HT_ERROR : HT_OK;
2.3 frystyk 156: }
157:
2.7 frystyk 158:
159: PRIVATE int HTFWriter_write ARGS3(HTStream *, me, CONST char*, s, int, l)
2.3 frystyk 160: {
2.7 frystyk 161: int status ;
162: status = (fwrite(s, 1, l, me->fp) != l) ? HT_ERROR : HT_OK ;
163: if (l > 1 && status == HT_OK)
164: (void)HTFWriter_flush( me) ;
165: return status ;
166:
167: /* return (fwrite(s, 1, l, me->fp) != l) ? HT_ERROR : HT_OK; */
168:
2.3 frystyk 169: }
2.7 frystyk 170:
2.3 frystyk 171:
172: PRIVATE int HTFWriter_free ARGS1(HTStream *, me)
173: {
174: if (me->leave_open != YES) fclose(me->fp);
175:
176: if (me->end_command) { /* Temp file */
177: system(me->end_command); /* @@ Beware of security hole */
178: free (me->end_command);
179: if (me->remove_on_close) {
2.9 frystyk 180: REMOVE(me->filename);
2.3 frystyk 181: }
182: }
183: if (me->callback) {
184: (*me->callback)(me->request, me->filename);
185: }
186: if (me->filename) free(me->filename);
187: free(me);
188: return HT_OK;
189: }
190:
191: PRIVATE int HTFWriter_abort ARGS2(HTStream *, me, HTError, e)
192: {
193: if (me->leave_open != YES) fclose(me->fp);
194: if (me->end_command) { /* Temp file */
195: if (TRACE)
196: fprintf(TDEST,"FileWriter.. Aborting: file %s not executed.\n",
197: me->filename ? me->filename : "???" );
198: free (me->end_command);
199: if (me->remove_on_close) {
2.9 frystyk 200: REMOVE(me->filename);
2.3 frystyk 201: }
202: }
203:
204: if (me->filename) free(me->filename);
205: free(me);
206: return HT_ERROR;
207: }
208:
209: PRIVATE CONST HTStreamClass HTFWriter = /* As opposed to print etc */
210: {
211: "FileWriter",
212: HTFWriter_flush,
213: HTFWriter_free,
214: HTFWriter_abort,
215: HTFWriter_put_character,
216: HTFWriter_put_string,
217: HTFWriter_write
218: };
219:
220: PUBLIC HTStream* HTFWriter_new ARGS2(FILE *, fp, BOOL, leave_open)
221: {
222: HTStream* me;
223:
224: if (!fp) {
225: if (TRACE)
226: fprintf(TDEST, "FileWriter.. Bad file descriptor\n");
227: return NULL;
228: }
229: me = (HTStream*)calloc(sizeof(*me),1);
230: if (me == NULL) outofmem(__FILE__, "HTFWriter_new");
231: me->isa = &HTFWriter;
232:
233: me->fp = fp;
234: me->leave_open = leave_open; /* HENRIK 08/02-94 */
235: me->end_command = NULL;
236: me->remove_on_close = NO;
237: me->announce = NO;
238: me->callback = NULL;
239: return me;
240: }
241:
242: /* ------------------------------------------------------------------------- */
243: /* FILE WRITER ROUTINES */
244: /* ------------------------------------------------------------------------- */
245:
246: /* Set TMP Root
247: ** --------------
248: ** If `tmp_root' is NULL use the current value (might be a define)
249: */
250: PUBLIC BOOL HTTmp_setRoot ARGS1(CONST char *, tmp_root)
251: {
252: StrAllocCopy(HTTmpRoot, tmp_root ? tmp_root : HT_TMP_ROOT);
253: if (*(HTTmpRoot+strlen(HTTmpRoot)-1) != '/')
254: StrAllocCat(HTTmpRoot, "/");
255: if (TRACE)
256: fprintf(TDEST, "Tmp Root.... Root set to `%s\'\n", HTTmpRoot);
257: return YES;
258: }
259:
260:
261: /* Get Tmp Root
262: ** --------------
2.1 frystyk 263: */
2.3 frystyk 264: PUBLIC CONST char * HTTmp_getRoot NOARGS
265: {
266: return HTTmpRoot;
267: }
2.1 frystyk 268:
269:
2.3 frystyk 270: /* Free Tmp Root
271: ** --------------
272: ** For clean up memory
273: */
274: PUBLIC void HTTmp_freeRoot NOARGS
275: {
276: FREE(HTTmpRoot);
277: }
2.1 frystyk 278:
2.4 frystyk 279: /*
280: ** This function tries really hard to find a non-existent filename relative
281: ** to the path given. Returns a string that must be freed by the caller or
282: ** NULL on error. The base must be '/' terminated which!
283: */
284: PRIVATE char *get_filename ARGS3(char *, base, CONST char *, url,
285: CONST char *, suffix)
286: {
287: char *path=NULL;
288: char filename[40];
289: int hash=0;
290:
291: /* Do this until we find a name that doesn't exist */
292: while (1)
293: {
294: CONST char *ptr=url;
295: for( ; *ptr; ptr++)
296: hash = (int) ((hash * 31 + (* (unsigned char *) ptr)) % HASH_SIZE);
297:
298: #ifndef NO_GETPID
299: sprintf(filename, "%d-%d", hash, (int) getpid());
300: #else
301: sprintf(filename, "%d-%d", hash, time(NULL));
302: #endif
303: StrAllocCopy(path, base);
304: StrAllocCat(path, filename);
305: if (suffix) StrAllocCat(path, suffix);
306:
307: {
308: FILE *fp = fopen(path, "r");
309: if (fp) /* This file does already exist */
310: fclose(fp);
311: else
312: break; /* Got the file name */
313: }
314: }
315: return path;
316: }
317:
318:
2.1 frystyk 319: /* Take action using a system command
320: ** ----------------------------------
321: **
322: ** Creates temporary file, writes to it, executes system command
323: ** on end-document. The suffix of the temp file can be given
324: ** in case the application is fussy, or so that a generic opener can
325: ** be used.
326: */
327: PUBLIC HTStream* HTSaveAndExecute ARGS5(
328: HTRequest *, request,
329: void *, param,
330: HTFormat, input_format,
331: HTFormat, output_format,
332: HTStream *, output_stream)
333: {
334: char *fnam;
335: HTStream* me;
336:
337: if (HTSecure) {
2.11 frystyk 338: HTAlert(request, "Can't save data to file -- please run WWW locally");
2.1 frystyk 339: return HTBlackHole();
340: }
341:
2.3 frystyk 342: if (!HTTmpRoot) {
2.1 frystyk 343: if (TRACE) fprintf(TDEST, "Save and execute turned off");
344: return HTBlackHole();
345: }
346:
2.4 frystyk 347: /* Let's find a hash name for this file */
348: {
2.13 ! frystyk 349: HTParentAnchor *anchor = (HTParentAnchor *) HTRequest_anchor(request);
! 350: char *suffix = HTBind_getSuffix(anchor);
! 351: fnam = get_filename(HTTmpRoot, HTAnchor_physical(anchor), suffix);
2.5 frystyk 352: FREE(suffix);
2.4 frystyk 353: if (!fnam) {
2.11 frystyk 354: HTAlert(request, "Can't find a suitable file name");
2.4 frystyk 355: return HTBlackHole();
356: }
357: }
358:
2.1 frystyk 359: me = (HTStream*)calloc(sizeof(*me), 1);
360: if (me == NULL) outofmem(__FILE__, "Save and execute");
361: me->isa = &HTFWriter;
362: me->request = request; /* won't be freed */
2.12 frystyk 363: me->fp = fopen (fnam, "wb");
2.1 frystyk 364: if (!me->fp) {
2.11 frystyk 365: HTAlert(request, "Can't open temporary file!");
2.1 frystyk 366: free(fnam);
367: free(me);
2.4 frystyk 368: return HTBlackHole();
2.1 frystyk 369: }
370: StrAllocCopy(me->filename, fnam);
371:
2.4 frystyk 372: /* Make command to process file */
2.1 frystyk 373: me->end_command = (char *) malloc ((strlen((char *) param) + 10 +
374: 3*strlen(fnam)) * sizeof (char));
375: if (me == NULL) outofmem(__FILE__, "SaveAndExecute");
376:
377: sprintf (me->end_command, (char *) param, fnam, fnam, fnam);
378: me->remove_on_close = NO;
379: me->announce = NO;
380: free (fnam);
381: return me;
382: }
383:
384:
385: /* Save Locally
386: ** ------------
387: **
388: ** Bugs:
389: ** GUI Apps should open local Save panel here really.
390: **
391: */
392: PUBLIC HTStream* HTSaveLocally ARGS5(
393: HTRequest *, request,
394: void *, param,
395: HTFormat, input_format,
396: HTFormat, output_format,
397: HTStream *, output_stream) /* Not used */
398:
399: {
400: char *fnam = NULL;
401: char *answer = NULL;
402: HTStream* me;
403:
2.8 frystyk 404: if (HTSecure) {
2.11 frystyk 405: HTAlert(request, "Can't save data to file -- please run WWW locally");
2.1 frystyk 406: return HTBlackHole();
407: }
408:
2.3 frystyk 409: if (!HTTmpRoot) {
2.1 frystyk 410: if (TRACE) fprintf(TDEST, "Save locally turned off");
411: return HTBlackHole();
412: }
413:
2.4 frystyk 414: /* Let's find a file name for this file */
415: {
2.13 ! frystyk 416: HTParentAnchor *anchor = (HTParentAnchor *) HTRequest_anchor(request);
! 417: char *suffix = HTBind_getSuffix(anchor);
! 418: fnam = get_filename(HTTmpRoot, HTAnchor_physical(anchor), suffix);
2.11 frystyk 419: answer = HTPrompt(request, "Give name of file to save in",
420: fnam ? fnam : "");
2.4 frystyk 421: if (!answer) {
422: FREE(fnam);
423: return HTBlackHole();
424: }
2.5 frystyk 425: FREE(suffix);
2.4 frystyk 426: FREE(fnam);
427: }
428:
2.1 frystyk 429: me = (HTStream*)calloc(sizeof(*me),1);
430: if (me == NULL) outofmem(__FILE__, "SaveLocally");
431: me->isa = &HTFWriter;
432: me->announce = YES;
433:
2.12 frystyk 434: me->fp = fopen (answer, "wb");
2.1 frystyk 435: if (!me->fp) {
2.11 frystyk 436: HTAlert(request, "Can't open local file to write into.");
2.1 frystyk 437: FREE(answer);
438: free(me);
2.4 frystyk 439: return HTBlackHole();
2.1 frystyk 440: }
441: me->callback = NULL;
442: me->request = request; /* won't be freed */
443: me->filename = answer; /* Will be freed */
444: return me;
445: }
446:
447:
448: /* Save and Call Back
449: ** ------------------
450: **
451: **
452: ** The special case is a kludge. Better is everything uses streams
453: ** and nothing uses files. Then this routine will go too. :-))
454: */
455: PUBLIC HTStream* HTSaveAndCallBack ARGS5(
456: HTRequest *, request,
457: void *, param,
458: HTFormat, input_format,
459: HTFormat, output_format,
460: HTStream *, output_stream)
461: {
462: HTStream * me;
463:
2.10 frystyk 464: #if 0
2.1 frystyk 465: if (request->using_cache) { /* Special case! file wanted && cache hit */
466: (*request->callback)(request,
2.13 ! frystyk 467: ((HTCache*) request->using_cache)->filename);
2.3 frystyk 468: return HTBlackHole();
2.1 frystyk 469: } else {
470: me = HTCacheWriter(request, param,
471: input_format, output_format, output_stream);
472: if (me) {
473: me->callback = request->callback;
474: }
475: }
2.10 frystyk 476: #endif
2.1 frystyk 477: return me;
478: }
479:
Webmaster