home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 1999 March B / SCO_CASTOR4RRT.iso / nsfast / root.9 / usr / ns-home / nsapi / examples / service.c / service
Text File  |  1998-08-19  |  9KB  |  309 lines

  1. /*
  2.  * Copyright (c) 1994, 1995, 1996.  Netscape Communications Corporation.
  3.  * All rights reserved.
  4.  * 
  5.  * Use of this software is governed by the terms of the license agreement
  6.  * for the Netscape Enterprise or Netscape Personal Server between the
  7.  * parties.
  8.  *
  9.  */
  10.  
  11.  
  12. /* ------------------------------------------------------------------------ */
  13.  
  14.  
  15. /*
  16.  * service.c: Example NSAPI functions to complete responses to requests
  17.  *
  18.  * The Server Application Functions in this file are Service class
  19.  * functions, and designed to demonstrate in general how to finish the
  20.  * server's response to a request.
  21.  * 
  22.  * Rob McCool
  23.  */
  24.  
  25.  
  26. /* 
  27.    The following three are standard headers for SAFs.  They're used to
  28.    get the data structures and prototypes needed to declare and use SAFs.
  29.  */
  30.  
  31.  
  32. #include "base/pblock.h"
  33. #include "base/session.h"
  34. #include "frame/req.h"
  35.  
  36. #if defined(SENDV)    /* for UnixWare (Gemini release) */
  37. #include <sys/types.h>
  38. #include <sys/sendv.h>
  39. #endif
  40.  
  41.  
  42. /* ----------------------------- send-images ------------------------------ */
  43.  
  44.  
  45. /*
  46.  
  47.    Users of Netscape 1.1 may be familiar with what those wacky client
  48.    guys are calling the "server push" version of dynamic document
  49.    refresh. One problem with doing this operation through CGI is that
  50.    it causes two processes, your CGI program and its corresponding
  51.    server process, to run. This can be wasteful. Further, your program
  52.    does not have direct access to the client socket and thus can't be
  53.    absolutely sure of when the client aborts a connection.
  54.  
  55.    This service class function is a replacement for the doit.cgi
  56.    demonstration available on the Netscape home pages. When accessed
  57.    as /dir1/dir2/foo.picgroup, the function will check if it's being
  58.    accessed by Mozilla/1.1. If not, it will send a short error
  59.    message. 
  60.  
  61.    foo.picgroup should be a simple file which lists on every line a
  62.    filename followed by a content-type. The filenames will be filtered
  63.    such that they may only refer to files in the current directory.
  64.  
  65.    Example .picgroup file:
  66.       one.gif image/gif
  67.       two.jpg image/jpeg
  68.       three.gif image/gif
  69.  
  70.    Usage:
  71.    At the beginning of obj.conf:
  72.       Init fn=load-modules shlib=example.<ext> funcs=send-images
  73.    In mime.types:
  74.       type=magnus-internal/picgroup exts=picgroup
  75.    Inside an object in obj.conf:
  76.       Service method=(GET|HEAD) type=magnus-internal/picgroup fn=send-images 
  77.  
  78.    <ext> = so on UNIX
  79.    <ext> = dll on NT.
  80.  
  81.    send-images also takes an optional parameter, delay, which specifies 
  82.    the number of seconds to pause between images.
  83.  
  84.  */
  85.  
  86.  
  87. #include "base/util.h"       /* is_mozilla, getline */
  88. #include "frame/protocol.h"  /* protocol_start_response */
  89. #include "base/file.h"       /* system_fopenRO */
  90. #include "base/buffer.h"     /* filebuf */
  91. #include "frame/log.h"       /* log_error */
  92.  
  93. #include <stdio.h>
  94. #ifndef XP_WIN32
  95. #include <unistd.h>  /* sleep */
  96. #define NSAPI_PUBLIC
  97. #else /* XP_WIN32 */
  98. #include <windows.h>
  99. #define NSAPI_PUBLIC __declspec(dllexport)
  100. #endif /* XP_WIN32 */
  101.  
  102. #define PICGROUP_MAXLINE 300
  103. #define DEFAULT_DELAY 1
  104.  
  105. #define FIRSTMSG "--AnUnlikelyOccurrence\n"
  106. #define NEWMSG "\n--AnUnlikelyOccurrence\n"
  107. #define MULTICT "multipart/x-mixed-replace; boundary=AnUnlikelyOccurrence"
  108. #define ERRMSG \
  109. "This feature requires <a href=\"ftp://ftp.netscape.com\">Netscape 1.1</a><p>"
  110.  
  111.  
  112. /*
  113.    This function sends the given file to Netscape
  114.  */
  115.  
  116. NSAPI_PUBLIC int _picgroup_send_file(char *dir, char *fn, char *ct, int delay,
  117.                         Session *sn, Request *rq)
  118. {
  119.     struct stat finfo;
  120.     filebuf *buf;
  121.     SYS_FILE fd;
  122.     int len, ret = REQ_PROCEED;
  123.     char ctbuf[PICGROUP_MAXLINE + sizeof("Content-type: ") + 2 + 1];
  124.  
  125.     char *pathname = (char *) MALLOC(strlen(dir) + strlen(fn) + 1);
  126. #if defined(SENDV)    /* for UnixWare (Gemini release) */
  127.     struct sendv_iovec iosv[3];
  128. #endif
  129.  
  130.     util_sprintf(pathname, "%s%s", dir, fn);
  131.  
  132.     /* If any errors, just skip it. The gotos are mostly for convenience. */
  133.     if(stat(pathname, &finfo) == -1)
  134.         goto done;
  135.  
  136.     fd = system_fopenRO(pathname);
  137.     if(fd == SYS_ERROR_FD)
  138.         goto done;
  139.  
  140. #if defined(SENDV)    /* for UnixWare (Gemini release) */
  141.  
  142.     len = util_sprintf(ctbuf, "Content-type: %s\nContent-length: %d\n\n",
  143.                         ct, finfo.st_size);
  144.  
  145.     iosv[0].sendv_flags = 0;
  146.     iosv[0].sendv_base = ctbuf;
  147.     iosv[0].sendv_len = len;
  148.     iosv[1].sendv_flags = SENDV_FD;
  149.     iosv[1].sendv_fd = fd;
  150.     iosv[1].sendv_off = 0;
  151.     iosv[1].sendv_len = finfo.st_size;
  152.     iosv[2].sendv_flags = 0;
  153.     iosv[2].sendv_base = NEWMSG;
  154.     iosv[2].sendv_len = strlen(NEWMSG);
  155.  
  156.     if (net_sendv(sn->csd, iosv, 3) == IO_ERROR)
  157.         ret = REQ_EXIT;
  158.  
  159. #else /* !SENDV */
  160.  
  161.     buf = filebuf_open_nostat(fd, FILE_BUFFERSIZE, &finfo);
  162.     if(!buf) {
  163.         system_fclose(fd);
  164.         goto done;
  165.     }
  166.     len = util_sprintf(ctbuf, "Content-type: %s\nContent-length: %d\n\n", 
  167.                        ct, finfo.st_size);
  168.     if(net_write(sn->csd, ctbuf, len) == IO_ERROR)
  169.         ret = REQ_EXIT;
  170.  
  171.     if(filebuf_buf2sd(buf, sn->csd) == IO_ERROR)
  172.         ret = REQ_EXIT;
  173.     filebuf_close(buf);
  174.  
  175.     if(net_write(sn->csd, NEWMSG, strlen(NEWMSG)) == IO_ERROR)
  176.         ret = REQ_EXIT;
  177. #endif /* !SENDV */
  178.     /* Pause to display */
  179. #ifdef XP_WIN32
  180.     Sleep(delay*1000);
  181. #else /* !XP_WIN32 */
  182.     sleep(delay);
  183. #endif /* !XP_WIN32 */
  184.  
  185.   done:
  186.     FREE(pathname);
  187.     return ret;
  188. }
  189.  
  190.  
  191. NSAPI_PUBLIC int send_images(pblock *pb, Session *sn, Request *rq)
  192. {
  193.     char *delaystr = pblock_findval("delay", pb);
  194.  
  195.     /* Server variables */
  196.     char *path = pblock_findval("path", rq->vars);
  197.  
  198.     /* Work variables */
  199.     filebuf *groupbuf;
  200.     SYS_FILE fd;
  201.     char *ua, *t, *base;
  202.     char line[PICGROUP_MAXLINE], *ct;
  203.     struct stat *fi;
  204.     int lnum, ret, delay;
  205.  
  206.     delay = (delaystr ? atoi(delaystr) : DEFAULT_DELAY);
  207.  
  208.     /* We need to get rid of the internal content type. */
  209.     param_free(pblock_remove("content-type", rq->srvhdrs));
  210.  
  211.     /* 
  212.        There's a chance somebody accidentally sent us path info.
  213.        If they did, send not found. 
  214.      */
  215.     if( (t = pblock_findval("path-info", rq->vars)) ) {
  216.         protocol_status(sn, rq, PROTOCOL_NOT_FOUND, NULL);
  217.         log_error(LOG_WARN, "send-images", sn, rq, "%s%s not found", path, t);
  218.         return REQ_ABORTED;
  219.     }
  220.  
  221.     /* 
  222.        Get the file's cached stat information, and open it. 
  223.        Errors mean it doesn't exist, or it can't be read
  224.      */
  225.     if( (!(fi = request_stat_path(path, rq)) ) || 
  226.         ( (fd = system_fopenRO(path)) == IO_ERROR) )
  227.     {
  228.         protocol_status(sn, rq, (file_notfound() ? PROTOCOL_NOT_FOUND : 
  229.                                  PROTOCOL_FORBIDDEN), NULL);
  230.         log_error(LOG_WARN, "send-images", sn, rq, "error opening %s (%s)", 
  231.                   path, system_errmsg(0));
  232.         return REQ_ABORTED;
  233.     }
  234.  
  235.     /* Use server native buffered I/O on the file */
  236.     groupbuf = filebuf_open_nostat(fd, FILE_BUFFERSIZE, fi);
  237.     if(!groupbuf) {
  238.         protocol_status(sn, rq, PROTOCOL_FORBIDDEN, NULL);
  239.         log_error(LOG_WARN,"send-file", sn, rq, 
  240.                   "error opening buffer from %s (%s)", path, 
  241.                   system_errmsg(fd));
  242.         system_fclose(fd);
  243.         return REQ_ABORTED;
  244.     }
  245.  
  246.     protocol_status(sn, rq, PROTOCOL_OK, NULL);
  247.  
  248.     /* Every time I have to check this variable I cringe. How revolting. */
  249.     if(request_header("user-agent", &ua, sn, rq) == REQ_ABORTED)
  250.         return REQ_ABORTED;
  251.     /* Check for Mozilla 1.1 or better */
  252.     if(!util_is_mozilla(ua, "1", "1")) {
  253.         /* Return a short error message */
  254.         pblock_nvinsert("content-type", "text/html", rq->srvhdrs);
  255.         if(protocol_start_response(sn, rq) != REQ_NOACTION)
  256.             (void) net_write(sn->csd, ERRMSG, strlen(ERRMSG));
  257.         return REQ_PROCEED;
  258.     }
  259.  
  260.     /* 
  261.        Normally, a call to stat followed by protocol_set_finfo would
  262.        be used here to handle conditional GET requests from the
  263.        browser. However, caching is not used for dynamic documents.
  264.      */
  265.  
  266.     /* Set content-type and begin response */
  267.     /* Technically, I should generate a random string here. */
  268.     pblock_nvinsert("content-type", MULTICT, rq->srvhdrs);
  269.  
  270.     /* A noaction response from this function means the request was HEAD */
  271.     if(protocol_start_response(sn, rq) == REQ_NOACTION) {
  272.         filebuf_close(groupbuf);  /* this also closes fd */
  273.         return REQ_PROCEED;
  274.     }
  275.  
  276.     /* Start response by giving boundary string */
  277.     if(net_write(sn->csd, FIRSTMSG, strlen(FIRSTMSG)) == IO_ERROR)
  278.         return REQ_EXIT;
  279.  
  280.     /* Get the base directory name */
  281.     base = STRDUP(path);
  282.     t = strrchr(base, FILE_PATHSEP);
  283.     *(t + 1) = '\0';
  284.  
  285.     for(ret = 0, lnum = 1; ret == 0; ++lnum) {
  286.         ret = util_getline(groupbuf, lnum, PICGROUP_MAXLINE, line);
  287.         if(ret != -1) {
  288.             /* If no space, invalid format. But just skip. */
  289.             if(!(ct = strchr(line, ' ')))
  290.                 continue;
  291.             *ct++ = '\0';
  292.  
  293.             /* Only files in this directory are allowed. */
  294.             if(strchr(line, FILE_PATHSEP))
  295.                 continue;
  296.  
  297.             /* Send a page break followed by the image */
  298.             if(_picgroup_send_file(base, line, ct, delay, sn, rq) == REQ_EXIT)
  299.                 ret = -1;
  300.  
  301.             /* Check for EOF */
  302.             if(ret == 1)
  303.                 ret = -1;
  304.         }
  305.     }
  306.     filebuf_close(groupbuf);
  307.     return REQ_PROCEED;
  308. }
  309.