/* Public domain */ #ifndef _PERCGI_PERCGI_H_ #define _PERCGI_PERCGI_H_ #include #if defined(HAVE_FASTCGI) || !defined(_PERCGI_NO_FASTCGI) # include /* FastCGI stdio wrappers */ #else # include #endif #include #include #define CGI_WORKER_WRBUFSIZE 8192 /* Master->Worker write buffer size */ #define CGI_WORKER_RDBUFSIZE 8192 /* Worker->Master read buffer size */ #define CGI_MAX_ARGS 256 /* URL-encoded argument count */ #define CGI_MAX_COOKIES 200 /* Maximum cookies */ #define CGI_ARG_KEY_MAX 256 /* Argument key length */ #define CGI_ARG_LENGTH_MAX 1000000000 /* Argument data length (1GB) */ #define CGI_LANGS_MAX 378 /* Accept-Language entries (per ISO639) */ #define CGI_LANG_CODE_MAX 6 /* Language code */ #define CGI_CHARSETS_MAX 245 /* Accept-Charset entries (per IANA) */ #define CGI_CHARSET_NAME_MAX 32 /* Character set name */ #define CGI_URL_MAX 4096 /* URL length */ #define CGI_ERROR_MAX 512 /* Error message length */ #define CGI_USERAGENT_MAX 512 /* Maximum HTTP User-Agent length */ #define CGI_OPNAME_MAX 32 /* Operation name length */ #define CGI_USERNAME_MAX 64 /* Username length */ #define CGI_PASSWORD_MAX 128 /* Password length */ #define CGI_SESSID_MAX 64 /* Session ID length */ #define CGI_EMAIL_MAX 128 /* E-mail address length */ #define CGI_INT_RANGE_MAX 21 /* Maximum integer range length */ #define CGI_HELP_TOPIC_MAX 80 #define CGI_COOKIE_NAME_MAX 48 /* Cookie name */ #define CGI_COOKIE_VALUE_MAX 3807 /* Cookie value */ #define CGI_COOKIE_EXPIRE_MAX 64 /* Cookie expiration field */ #define CGI_COOKIE_DOMAIN_MAX 48 /* Cookie domain field */ #define CGI_COOKIE_PATH_MAX 128 /* Cookie path argument */ #ifndef CGI_GLYPHICON #define CGI_GLYPHICON(x) "" #endif struct cgi_query; struct cgi_session; struct cgi_session_ops; struct var; /* Command function */ typedef int (*CGI_CommandFn)(struct cgi_query *); /* Content-specific filter */ typedef struct cgi_filter { enum cgi_filter_type { CGI_INPUT_FILTER, /* Filter data read by the script */ CGI_OUTPUT_FILTER /* Filter data written to stdout */ } type; const char *content_type; size_t (*func)(struct cgi_query *, void **, size_t); TAILQ_ENTRY(cgi_filter) filters; } CGI_Filter; /* Argument to script (key=value pair). */ typedef struct cgi_argument { enum cgi_argument_type { CGI_GET_ARGUMENT, /* HTTP GET method */ CGI_POST_ARGUMENT, /* HTTP POST method */ CGI_ARGUMENT_LAST } type; char key[CGI_ARG_KEY_MAX]; /* Key */ char *value; /* Value data (allocated) */ size_t len; /* Value length in bytes */ char contentType[32]; /* Content-Type or "" */ TAILQ_ENTRY(cgi_argument) args; } CGI_Argument; /* HTTP cookie */ typedef struct cgi_cookie { /* (Read from client) */ char name[CGI_COOKIE_NAME_MAX]; /* Name of cookie */ char value[CGI_COOKIE_VALUE_MAX]; /* Value of cookie */ /* (for Set-Cookie: output) */ char expires[CGI_COOKIE_EXPIRE_MAX]; /* Expiration date or \0 */ char domain[CGI_COOKIE_DOMAIN_MAX]; /* Domain match or \0 */ char path[CGI_COOKIE_PATH_MAX]; /* Path attribute or \0 */ Uint flags; #define CGI_COOKIE_SECURE 0x01 /* Secure attribute */ #define CGI_COOKIE_HTTPONLY 0x02 /* HttpOnly attribute */ TAILQ_ENTRY(cgi_cookie) cookies; } CGI_Cookie; /* Query from web server */ typedef struct cgi_query { /* From query */ char *url; /* URL of application */ char *acceptLangs[CGI_LANGS_MAX]; /* Accepted languages */ Uint nAcceptLangs; char *acceptCharsets[CGI_CHARSETS_MAX]; /* Accepted character sets */ Uint nAcceptCharsets; TAILQ_HEAD(,cgi_argument) args; /* GET/POST arguments */ Uint nArgs; TAILQ_HEAD(,cgi_cookie) cookies; /* HTTP cookies */ Uint nCookies; char contentType[64]; /* Client content-type */ int contentRead; /* Content has been read */ /* For response */ const char *wrType; /* Content-Type for write */ char lang[4]; /* Negotiated language */ char cset[16]; /* Negotiated character set */ char html_dir[FILENAME_MAX]; /* Directory for HTML data */ void *sess; /* Session object (or NULL) */ int sock; /* Output socket (or -1) */ } CGI_Query; /* Set of keys */ typedef struct cgi_keyset { char **ents; int nents; } CGI_KeySet; /* Description of user-provided option flags */ typedef struct cgi_flag_descr { Uint bitmask; const char *name; const char *permsRD; const char *permsWR; const char *descr; } CGI_FlagDescr; /* Log levels; sync with cgiLogLvlNames[] */ enum cgi_loglvl { CGI_LOG_EMERG, CGI_LOG_ALERT, CGI_LOG_CRIT, CGI_LOG_ERR, CGI_LOG_WARNING, CGI_LOG_NOTICE, CGI_LOG_INFO, CGI_LOG_DEBUG, CGI_LOG_QUERY }; /* Application data */ typedef struct cgi_application { const char *name; /* Description */ const char *copyright; /* Copyright notice */ const char *availLangs[CGI_LANGS_MAX]; /* Available languages */ const char *homeOp; /* Default operation */ const char *urlBase; /* Force URL base (NULL=auto) */ Uint flags; #define CGI_PERSISTENT 0x02 /* Only run under FastCGI */ #define CGI_PRETTY_URL 0x04 /* Use "/op?args" instead of "/path/to/prog.fcgi?op=op&args" */ void (*destroyFn)(void); void (*logFn)(enum cgi_loglvl, const char *s); /* Private */ char *availCharsets[CGI_CHARSETS_MAX]; /* Available character sets */ TAILQ_HEAD(,cgi_filter) filters; /* Content filter chain */ TAILQ_HEAD(,var) vars; /* User variables */ Uint queryCount; /* Queries served */ } CGI_Application; /* Query processing command */ typedef struct cgi_command { char *name; /* Command name */ CGI_CommandFn fn; /* Function */ const char *type; /* MIME type (or NULL) */ } CGI_Command; typedef struct cgi_module_section { const char *name; const char *cmd; } CGI_Section; /* Query processing module */ typedef struct cgi_module { char *name; /* Short name */ char *icon; /* Icon (HTML) */ char *lname; /* Long name (HTML) */ char *desc; /* Description (HTML) */ int (*init)(void *sess); void (*destroy)(void); int (*sessOpen)(void *sess); void (*sessClose)(void *sess); int (*indexFn)(CGI_Query *q); void (*menu)(CGI_Query *q, struct var *v); CGI_Command *commands; CGI_Section *sections; } CGI_Module; typedef int (*CGI_LanguageFn)(const char *, void *); typedef void (*CGI_MenuFn)(struct cgi_query *, struct var *, void *); #define CGI_SESSION(p) ((CGI_Session *)(p)) #define CGI_SESSID(p) (CGI_SESSION(p)->id) #if defined(_PERCGI_INTERNAL) || defined(_USE_PERCGI_STD) #define SESSION(p) CGI_SESSION(p) #define SESSID(p) CGI_SESSID(p) #endif #define CGI_TRIM_WHITESPACE(s,end) \ while (isspace(*s)) { s++; } \ if (*s != '\0') { \ end = &s[strlen(s) - 1]; \ while (end > s && isspace(*end)) { \ *end = '\0'; \ end--; \ } \ } __BEGIN_DECLS extern CGI_Application *cgi; extern CGI_Module **cgiModules; extern int nCgiModules; void CGI_Init(CGI_Application *); void CGI_Destroy(void); void CGI_CheckSignals(void); void CGI_RegisterModule(CGI_Module *); void CGI_SetLogFile(const char *); void CGI_Exit(int, const char *, ...); void CGI_OutOfMem(void); char *CGI_GetError(void); void CGI_SetError(const char *, ...) FORMAT_ATTRIBUTE(__printf__, 1, 2) NONNULL_ATTRIBUTE(1); void CGI_SetLanguageFn(CGI_LanguageFn, void *); void CGI_SetMenuFn(CGI_MenuFn, void *); int CGI_ValidOp(const char *, const struct cgi_session_ops *); void CGI_QueryInit(CGI_Query *); void CGI_QueryDestroy(CGI_Query *); int CGI_QueryLoad(int, CGI_Query *); int CGI_QuerySave(int, const CGI_Query *); int CGI_QueryReadHTTP(CGI_Query *, const struct cgi_session_ops *); void CGI_BeginQuery(CGI_Query *, const char *); void CGI_BeginQueryUnauth(CGI_Query *, const char *, struct cgi_session_ops *); void CGI_WriteHeader(CGI_Query *, const char *, const char *, int); ssize_t CGI_WriteFiltered(CGI_Query *, void *, size_t); #define CGI_WriteHeaderHTML(q) \ CGI_WriteHeader((q),"text/html","utf-8",1) int CGI_ParseForm_URLENC(CGI_Query *, const char *, enum cgi_argument_type); int CGI_ReadForm_URLENC(CGI_Query *); int CGI_ReadForm_FORMDATA(CGI_Query *, const char *); const char *CGI_Get(CGI_Query *, const char *, size_t); const char *CGI_GetTrim(CGI_Query *, const char *, size_t); int CGI_GetInt(CGI_Query *, const char *, int *); int CGI_GetUint(CGI_Query *, const char *, Uint *); int CGI_GetIntR(CGI_Query *, const char *, int *, int, int); int CGI_GetUintR(CGI_Query *, const char *, Uint *, Uint); int CGI_GetIntRange(CGI_Query *, const char *, int *, const char *, int *); int CGI_GetUint64(CGI_Query *, const char *, Uint64 *); int CGI_GetSint64(CGI_Query *, const char *, Sint64 *); int CGI_GetEnum(CGI_Query *, const char *, Uint *, Uint); int CGI_GetFloat(CGI_Query *, const char *, float *); int CGI_GetDouble(CGI_Query *, const char *, double *); int CGI_GetBool(CGI_Query *, const char *); void CGI_Set(CGI_Query *, const char *, const char *, ...); void CGI_SetS(CGI_Query *, const char *, const char *); int CGI_Unset(CGI_Query *, const char *); void CGI_AddFilter(CGI_Filter *); void CGI_DelFilter(CGI_Filter *); char *CGI_EscapeURL(CGI_Query *, const char *); char *CGI_UnescapeURL(CGI_Query *, const char *); void CGI_Printf(CGI_Query *, const char *, ...) FORMAT_ATTRIBUTE(__printf__, 2, 3) NONNULL_ATTRIBUTE(2); void CGI_Log(enum cgi_loglvl, const char *, ...) FORMAT_ATTRIBUTE(__printf__, 2, 3) NONNULL_ATTRIBUTE(2); void CGI_LogS(enum cgi_loglvl, const char *) NONNULL_ATTRIBUTE(2); void CGI_LogErr(const char *, ...) FORMAT_ATTRIBUTE(__printf__,1,2) NONNULL_ATTRIBUTE(1); void CGI_LogWarn(const char *, ...) FORMAT_ATTRIBUTE(__printf__,1,2) NONNULL_ATTRIBUTE(1); void CGI_LogInfo(const char *, ...) FORMAT_ATTRIBUTE(__printf__,1,2) NONNULL_ATTRIBUTE(1); void CGI_LogNotice(const char *, ...) FORMAT_ATTRIBUTE(__printf__,1,2) NONNULL_ATTRIBUTE(1); void CGI_LogDebug(const char *, ...) FORMAT_ATTRIBUTE(__printf__,1,2) NONNULL_ATTRIBUTE(1); CGI_KeySet *CGI_GetKeySet(CGI_Query *, const char *); void CGI_FreeKeySet(CGI_KeySet *); CGI_Cookie *CGI_SetCookie(CGI_Query *, const char *, const char *, ...); CGI_Cookie *CGI_SetCookieS(CGI_Query *, const char *, const char *); void CGI_DelCookie(CGI_Query *, const char *); void CGI_ClearCookies(CGI_Query *); void CGI_PutJSON(CGI_Query *, const char *, const char *, ...) FORMAT_ATTRIBUTE(__printf__, 3, 4) NONNULL_ATTRIBUTE(3); int CGI_PutJSON_HTML(CGI_Query *, const char *, const char *) NONNULL_ATTRIBUTE(2) NONNULL_ATTRIBUTE(3); static __inline__ void * CGI_Malloc(size_t len) { void *buf; if ((buf = malloc(len)) == NULL) { CGI_OutOfMem(); } return (buf); } static __inline__ void * CGI_TryMalloc(size_t len) { void *buf; if ((buf = malloc(len)) == NULL) { CGI_SetError("Out of memory"); return (NULL); } return (buf); } static __inline__ void * CGI_Realloc(void *p, size_t len) { void *buf; buf = (p == NULL) ? malloc(len) : realloc(p,len); if (buf == NULL) { CGI_OutOfMem(); } return (buf); } static __inline__ void * CGI_TryRealloc(void *p, size_t len) { void *buf; buf = (p == NULL) ? malloc(len) : realloc(p,len); if (buf == NULL) { CGI_SetError("Out of memory"); return (NULL); } return (buf); } static __inline__ void CGI_Free(void *p) { if (p != NULL) { free(p); } } /* Get a pointer to the named argument. Return NULL if undefined. */ static __inline__ CGI_Argument * CGI_GetArgument(CGI_Query *q, const char *key) { CGI_Argument *arg; TAILQ_FOREACH(arg, &q->args, args) { if (strcmp(arg->key, key) == 0) break; } if (arg == NULL) { CGI_SetError("Argument \"%s\" is missing", key); } return (arg); } static __inline__ int SYS_Read(int fd, void *data, size_t len) { size_t nread; ssize_t rv; for (nread = 0; nread < len; ) { rv = read(fd, data+nread, len-nread); if (rv == -1) { if (errno == EINTR || errno == EAGAIN) { CGI_CheckSignals(); continue; } else { CGI_SetError("Read error: %s", strerror(errno)); return (-1); } } else if (rv == 0) { CGI_SetError("EOF"); return (-1); } nread += rv; } return (0); } static __inline__ int SYS_Write(int fd, const void *data, size_t len) { size_t nwrote; ssize_t rv; for (nwrote = 0; nwrote < len; ) { rv = write(fd, data+nwrote, len-nwrote); if (rv == -1) { if (errno == EINTR || errno == EAGAIN) { CGI_CheckSignals(); continue; } else { CGI_SetError("Write error: %s", strerror(errno)); return (-1); } } else if (rv == 0) { CGI_SetError("EOF"); return (-1); } nwrote += rv; } return (0); } static __inline__ int CGI_Read(CGI_Query *q, void *data, size_t len) { if (q->sock != -1) { return SYS_Read(q->sock, data, len); } else { clearerr(stdin); if (fread(data, len, 1, stdin) < 1) { CGI_SetError("fread: %s", feof(stdin) ? "EOF" : "Error"); return (-1); } } return (0); } static __inline__ int CGI_Write(CGI_Query *q, const void *data, size_t len) { if (q->sock != -1) { return SYS_Write(q->sock, data, len); } else { clearerr(stdout); /* XXX FCGI_fwrite const */ if (fwrite((void *)data, len, 1, stdout) < 1) { CGI_SetError("fwrite: %s", feof(stdout) ? "EOF" : "Error"); return (-1); } } return (0); } static __inline__ void CGI_PutC(CGI_Query *q, char c) { CGI_Write(q, &c, 1); } static __inline__ void CGI_PutS(CGI_Query *q, const char *s) { CGI_Write(q, s, strlen(s)); } static __inline__ void CGI_PutJSON_S(CGI_Query *q, const char *key, const char *val) { const char *c; CGI_PutC(q, '"'); CGI_PutS(q, key); CGI_PutS(q, "\": \""); for (c = &val[0]; *c != '\0'; c++) { if (*c == '\\') { CGI_PutS(q, "\\\\"); } else if (*c == '"') { CGI_PutS(q, "\\\""); } else if (*c == '\r') { CGI_PutS(q, "\\r"); } else if (*c == '\n') { CGI_PutS(q, "\\n"); } else if (*c == '\t') { CGI_PutS(q, "\\t"); } else { CGI_PutC(q, *c); } } CGI_PutS(q, "\","); } static __inline__ void CGI_PutJSON_NoHTML_S(CGI_Query *q, const char *key, const char *val) { const char *c; CGI_PutC(q, '"'); CGI_PutS(q, key); CGI_PutS(q, "\": \""); for (c = &val[0]; *c != '\0'; c++) { if (*c == '\\') { CGI_PutS(q, "\\\\"); } else if (*c == '"') { CGI_PutS(q, "\\\""); } else if (*c == '\n') { CGI_PutS(q, "\\n"); } else if (*c == '\t') { CGI_PutS(q, "\\t"); } else if (*c == '<') { CGI_PutS(q, "<"); } else if (*c == '>') { CGI_PutS(q, ">"); } else { CGI_PutC(q, *c); } } CGI_PutS(q, "\","); } /* Lookup a cookie by name. */ static __inline__ char * CGI_GetCookie(CGI_Query *q, const char *name) { CGI_Cookie *ck; TAILQ_FOREACH(ck, &q->cookies, cookies) { if (strcmp(ck->name, name) == 0) return (ck->value); } return (NULL); } static __inline__ CGI_Cookie * CGI_LookupCookie(CGI_Query *q, const char *name) { CGI_Cookie *ck; TAILQ_FOREACH(ck, &q->cookies, cookies) { if (strcmp(ck->name, name) == 0) return (ck); } return (NULL); } __END_DECLS #include #include #include #include #include #include #include #include #include __BEGIN_DECLS int CGI_WorkerMain(CGI_SessionOps *, CGI_Query *, const char *, const char *, const char *, int [2], int); void CGI_QueryLoop(CGI_SessionOps *); __END_DECLS #include #endif /* _PERCGI_PERCGI_H_ */