1 /***************************************************************
2 Copyright (C) 2011-2012 Hewlett-Packard Development Company, L.P.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 version 2 as published by the Free Software Foundation.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License along
14 with this program; if not, write to the Free Software Foundation, Inc.,
15 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 ***************************************************************/
17
18 /**
19 * \file
20 * \brief Get mime type for specified package
21 */
22
23 #include "finder.h"
24
25 char SQL[MAXCMD]; ///< For DB
26
27 PGresult *DBMime = NULL; ///< contents of mimetype table
28 int MaxDBMime=0; ///< how many rows in DBMime
29 PGconn *pgConn;
30 int Agent_pk=-1; ///< agent identifier
31
32 FILE *FMimetype=NULL; ///< for /etc/mime.types
33
34 magic_t MagicCookie; ///< for Magic
35
36 int Akey = 0;
37 char A[MAXCMD]; ///< input for this system
38
39 /**
40 * \brief Create a string with taint quoting.
41 *
42 * \param[in] S The string to be tainted
43 *
44 * \return Static tainted string.
45 */
TaintString(char * S)46 char * TaintString(char *S)
47 {
48 static char String[4096];
49 int i;
50
51 memset(String,'\0',sizeof(String));
52 if (!S) return(String);
53 for(i=0; (S[0]!='\0') && (i < sizeof(String)-1); S++)
54 {
55 if (S[0]=='\n') { String[i++]='\\'; String[i++]='n'; }
56 else if (S[0]=='\r') { String[i++]='\\'; String[i++]='r'; }
57 else if (S[0]=='\a') { String[i++]='\\'; String[i++]='a'; }
58 else if (S[0]=='\'') { String[i++]='\\'; String[i++]='\''; }
59 else if (S[0]=='\"') { String[i++]='\\'; String[i++]='"'; }
60 else if (S[0]=='\\') { String[i++]='\\'; String[i++]='\\'; }
61 else String[i++]=S[0];
62 }
63 return(String);
64 } /* TaintString() */
65
66 /**
67 * \brief Populate the DBMime table.
68 */
DBLoadMime()69 void DBLoadMime()
70 {
71 if (DBMime) PQclear(DBMime);
72 memset(SQL, 0, MAXCMD);
73 snprintf(SQL, MAXCMD-1, "SELECT mimetype_pk,mimetype_name FROM mimetype ORDER BY mimetype_pk ASC;");
74 DBMime = PQexec(pgConn, SQL); /* SELECT */
75 if (fo_checkPQresult(pgConn, DBMime, SQL, __FILE__, __LINE__))
76 {
77 PQfinish(pgConn);
78 exit(-1);
79 }
80 MaxDBMime = PQntuples(DBMime);
81 } /* DBLoadMime() */
82
83 /**
84 * \brief Find a mime type in the DBMime table.
85 *
86 * If the Mimetype is already in table mimetype, return mimetype_pk,
87 * if not, insert it into table mimetype, then return the mimetype_pk.
88 *
89 * \param Mimetype Mimetype_name
90 * \return int - mimetype ID or -1 if not found.
91 */
DBFindMime(char * Mimetype)92 int DBFindMime(char *Mimetype)
93 {
94 int i;
95 PGresult *result;
96
97 if (!Mimetype || (Mimetype[0]=='\0')) return(-1);
98 if (!DBMime) DBLoadMime();
99 for(i=0; i < MaxDBMime; i++)
100 {
101 if (!strcmp(Mimetype,PQgetvalue(DBMime,i,1)))
102 {
103 return(atoi(PQgetvalue(DBMime,i,0))); /* return mime type */
104 }
105 }
106
107 /* If it got here, then the mimetype is unknown. Add it! */
108 memset(SQL,'\0',sizeof(SQL));
109 snprintf(SQL,sizeof(SQL)-1,"INSERT INTO mimetype (mimetype_name) VALUES ('%s');",TaintString(Mimetype));
110 /* The insert will fail if it already exists. This is good. It will
111 prevent multiple mimetype agents from inserting the same data at the
112 same type. */
113 result = PQexec(pgConn, SQL);
114 if ((result==0) || ((PQresultStatus(result) != PGRES_COMMAND_OK) &&
115 (strncmp("23505", PQresultErrorField(result, PG_DIAG_SQLSTATE),5))))
116 {
117 PQfinish(pgConn);
118 exit(-1);
119 }
120 PQclear(result);
121
122 /* Now reload the mimetype table */
123 DBLoadMime();
124 /* And re-process the request... */
125 return(DBFindMime(Mimetype));
126 } /* DBFindMime() */
127
128 /**
129 * \brief Given an extension, see if extension exists in
130 * the /etc/mime.types.
131 *
132 * \param Ext The extension
133 *
134 * \return int - if the extension exists in
135 * the /etc/mime.types, add metatype to DB and return
136 * DB index. Otherwise, return -1.
137 */
CheckMimeTypes(char * Ext)138 int CheckMimeTypes(char *Ext)
139 {
140 char Line[MAXCMD];
141 int i;
142 int ExtLen;
143
144 if (!FMimetype) return(-1);
145 if (!Ext || (Ext[0] == '\0')) return(-1);
146 ExtLen = strlen(Ext);
147 rewind(FMimetype);
148 LOG_VERBOSE0("Looking for mimetype based on extension: '%s'",Ext);
149
150 while(ReadLine(FMimetype,Line,MAXCMD) > 0)
151 {
152 if (Line[0] == '#') continue; /* skip comments */
153 /* find the extension */
154 for(i=0; (Line[i] != '\0') && !isspace(Line[i]); i++);
155 if (Line[i] == '\0') continue; /* no file types */
156 Line[i]='\0'; /* terminate the metatype */
157 i++;
158
159 /* Now find the extensions and see if any match */
160 #if 0
161 printf("CheckMimeTypes(%s) in '%s' from '%s\n",Ext,Line+i,Line);
162 #endif
163 for( ; Line[i] != '\0'; i++)
164 {
165 /* Line[i-1] is always valid */
166 /* if the previous character is not a word-space, then skip */
167 if ((Line[i-1] != '\0') && !isspace(Line[i-1]))
168 continue; /* not start of a type */
169 /* if the first character does not match is a shortcut.
170 if the string matches AND the next character is a word-space,
171 then match. */
172 if ((Line[i] == Ext[0]) && !strncasecmp(Line+i,Ext,ExtLen) &&
173 ( (Line[i+ExtLen] == '\0') || isspace(Line[i+ExtLen]) )
174 )
175 {
176 /* it matched! */
177 LOG_VERBOSE0("Found mimetype by extension: '%s' = '%s'",Ext,Line);
178 return(DBFindMime(Line)); /* return metatype id */
179 }
180 }
181 }
182
183 /* For specagent (used because the DB query 'like %.spec' is slow) */
184 if (!strcasecmp(Ext,"spec")) return(DBFindMime("application/x-rpm-spec"));
185
186 return(-1);
187 } /* CheckMimeTypes() */
188
189 /**
190 * \brief Given a pfile, identify any filenames
191 * and see if any of them have a known extension based on
192 * /etc/mime.types.
193 * \note Reads pfile id from Akey
194 * \return int - return the mimetype id, or -1 if not found.
195 */
DBCheckFileExtention()196 int DBCheckFileExtention()
197 {
198 int u, Maxu;
199 char *Ext;
200 int rc;
201 PGresult *result;
202
203 if (!FMimetype) return(-1);
204
205 if (Akey >= 0)
206 {
207 memset(SQL,'\0',sizeof(SQL));
208 snprintf(SQL,sizeof(SQL)-1,"SELECT distinct(ufile_name) FROM uploadtree WHERE pfile_fk = %d",Akey);
209 result = PQexec(pgConn, SQL);
210 if (fo_checkPQresult(pgConn, result, SQL, __FILE__, __LINE__))
211 {
212 PQfinish(pgConn);
213 exit(-1);
214 }
215
216 Maxu = PQntuples(result);
217 for(u=0; u<Maxu; u++)
218 {
219 Ext = strrchr(PQgetvalue(result,u,0),'.'); /* find the extention */
220 if (Ext)
221 {
222 Ext++; /* move past period */
223 rc = CheckMimeTypes(Ext);
224 if (rc >= 0) return(rc);
225 }
226 }
227 PQclear(result);
228 } /* if using DB */
229 else
230 {
231 /* using command-line */
232 Ext = strrchr(A,'.'); /* find the extention */
233 if (Ext)
234 {
235 Ext++; /* move past period */
236 rc = CheckMimeTypes(Ext);
237 if (rc >= 0) return(rc);
238 }
239 }
240 return(-1);
241 } /* DBCheckFileExtention() */
242
243 /**
244 * \brief Get the ID for the default mimetype.
245 *
246 * Options are:
247 * | Mimetype | Description |
248 * | ---: | :--- |
249 * | application/x-empty | Zero-length file |
250 * | text/plain | 1st 100 characters are printable |
251 * | application/octet-stream | 1st 100 characters contain binary |
252 *
253 * \param MimeType Mimetype name
254 * \param Filename File name
255 *
256 * \return int - return -1 on error, or DB index to metatype.
257 */
GetDefaultMime(char * MimeType,char * Filename)258 int GetDefaultMime(char *MimeType, char *Filename)
259 {
260 int i;
261 FILE *Fin;
262 int C;
263
264 /* the common case: the default mime type is known already */
265 if (MimeType) return(DBFindMime(MimeType));
266
267 /* unknown mime, so find out what it is... */
268 Fin = fopen(Filename,"rb");
269 if (!Fin) return(-1);
270
271 i=0;
272 C=fgetc(Fin);
273 while(!feof(Fin) && isprint(C) && (i < 100))
274 {
275 C=fgetc(Fin);
276 i++;
277 }
278 fclose(Fin);
279
280 if (i==0) return(DBFindMime("application/x-empty"));
281 if ((C >= 0) && !isprint(C)) return(DBFindMime("application/octet-stream"));
282 return(DBFindMime("text/plain"));
283 } /* GetDefaultMime() */
284
285 /**
286 * \brief Given a file, check if it has a mime type
287 * in the DB.
288 *
289 * If it does not, then add it.
290 * \param Filename The path of the file
291 */
DBCheckMime(char * Filename)292 void DBCheckMime(char *Filename)
293 {
294 char MimeType[MAXCMD];
295 char *MagicType;
296 int MimeTypeID;
297 int i;
298 PGresult *result;
299
300 if (Akey >= 0)
301 {
302 memset(SQL,'\0',sizeof(SQL));
303 snprintf(SQL,sizeof(SQL)-1,"SELECT pfile_mimetypefk FROM pfile WHERE pfile_pk = %d AND pfile_mimetypefk is not null;",Akey);
304 result = PQexec(pgConn, SQL);
305 if (fo_checkPQresult(pgConn, result, SQL, __FILE__, __LINE__))
306 {
307 PQfinish(pgConn);
308 exit(-1);
309 }
310
311 if (PQntuples(result) > 0)
312 {
313 PQclear(result);
314 return;
315 }
316 PQclear(result);
317 } /* if using DB */
318
319 /* Not in DB, so find out what it is... */
320 /* Check using Magic */
321 MagicType = (char *)magic_file(MagicCookie,Filename);
322 memset(MimeType,'\0',MAXCMD);
323 if (MagicType)
324 {
325 LOG_VERBOSE0("Found mimetype by magic: '%s'",MagicType);
326 /* Magic contains additional data after a ';' */
327 for(i=0;
328 (i<MAXCMD) && (MagicType[i] != '\0') &&
329 !isspace(MagicType[i]) && !strchr(",;",MagicType[i]);
330 i++)
331 {
332 MimeType[i] = MagicType[i];
333 }
334 if (!strchr(MimeType,'/')) { memset(MimeType,'\0',MAXCMD); }
335 }
336
337 /* If there is no mimetype, or there is one but it is a default value,
338 then determine based on extension */
339 if (!strcmp(MimeType,"text/plain") || !strcmp(MimeType,"application/octet-stream") || (MimeType[0]=='\0'))
340 {
341 /* unknown type... Guess based on file extention */
342 MimeTypeID = DBCheckFileExtention();
343 /* not known? */
344 if (MimeTypeID < 0) MimeTypeID = GetDefaultMime(MimeType,Filename);
345 }
346 else
347 {
348 /* We have a mime-type! Update the database */
349 MimeTypeID = DBFindMime(MimeType);
350 }
351
352 /* Make sure there is a mime-type */
353 if (MimeTypeID < 0)
354 {
355 /* This should never happen; give it a default. */
356 MimeTypeID = DBFindMime("application/octet-stream");
357 }
358
359 /* Update pfile record */
360 if (Akey >= 0)
361 {
362 result = PQexec(pgConn, "BEGIN;");
363 if (fo_checkPQcommand(pgConn, result, SQL, __FILE__, __LINE__))
364 {
365 PQfinish(pgConn);
366 exit(-1);
367 }
368
369 memset(SQL,'\0',sizeof(SQL));
370 snprintf(SQL,sizeof(SQL)-1,"SELECT * FROM pfile WHERE pfile_pk = %d FOR UPDATE;",Akey);
371 PQclear(result);
372 result = PQexec(pgConn, SQL);
373 if (fo_checkPQresult(pgConn, result, SQL, __FILE__, __LINE__))
374 {
375 PQfinish(pgConn);
376 exit(-1);
377 }
378
379 memset(SQL,'\0',sizeof(SQL));
380 snprintf(SQL,sizeof(SQL)-1,"UPDATE pfile SET pfile_mimetypefk = %d WHERE pfile_pk = %d;",MimeTypeID,Akey);
381 PQclear(result);
382 result = PQexec(pgConn, SQL);
383 if (fo_checkPQcommand(pgConn, result, SQL, __FILE__, __LINE__))
384 {
385 PQfinish(pgConn);
386 exit(-1);
387 }
388
389 PQclear(result);
390 result = PQexec(pgConn, "COMMIT;");
391 if (fo_checkPQcommand(pgConn, result, SQL, __FILE__, __LINE__))
392 {
393 PQfinish(pgConn);
394 exit(-1);
395 }
396 PQclear(result);
397 }
398 else
399 {
400 /* IF no Akey, then display to stdout */
401 int i;
402 for(i=0; i < MaxDBMime; i++)
403 {
404 if (MimeTypeID == atoi(PQgetvalue(DBMime,i,0)))
405 {
406 printf("%s : mimetype_pk=%d : ",PQgetvalue(DBMime,i,1),MimeTypeID);
407 }
408 }
409 printf("%s\n",Filename);
410 }
411 } /* DBCheckMime() */
412
413 /**
414 * \brief Given a string that contains
415 * field='value' pairs, save the items.
416 * \param[in] Sin Input string
417 * \param[out] Field Field string
418 * \param[in] FieldMax Capacity of Field
419 * \param[out] Value Value string
420 * \param[in] ValueMax Capacity of Field
421 *
422 * \return Return character pointer to start of next field, or
423 * NULL at \0.
424 */
GetFieldValue(char * Sin,char * Field,int FieldMax,char * Value,int ValueMax)425 char * GetFieldValue (char *Sin, char *Field, int FieldMax,
426 char *Value, int ValueMax)
427 {
428 int s,f,v;
429 int GotQuote;
430
431 memset(Field,0,FieldMax);
432 memset(Value,0,ValueMax);
433
434 while(isspace(Sin[0])) Sin++; /* skip initial spaces */
435 if (Sin[0]=='\0') return(NULL);
436 f=0; v=0;
437
438 for(s=0; (Sin[s] != '\0') && !isspace(Sin[s]) && (Sin[s] != '='); s++)
439 {
440 Field[f++] = Sin[s];
441 }
442 while(isspace(Sin[s])) s++; /* skip spaces after field name */
443 if (Sin[s] != '=') /* if it is not a field, then just return it. */
444 {
445 return(Sin+s);
446 }
447 if (Sin[s]=='\0') return(NULL);
448 s++; /* skip '=' */
449 while(isspace(Sin[s])) s++; /* skip spaces after '=' */
450 if (Sin[s]=='\0') return(NULL);
451
452 GotQuote='\0';
453 if ((Sin[s]=='\'') || (Sin[s]=='"'))
454 {
455 GotQuote = Sin[s];
456 s++; /* skip quote */
457 if (Sin[s]=='\0') return(NULL);
458 }
459 if (GotQuote)
460 {
461 for( ; (Sin[s] != '\0') && (Sin[s] != GotQuote); s++)
462 {
463 if (Sin[s]=='\\') Value[v++]=Sin[++s];
464 else Value[v++]=Sin[s];
465 }
466 }
467 else
468 {
469 /* if it gets here, then there is no quote */
470 for( ; (Sin[s] != '\0') && !isspace(Sin[s]); s++)
471 {
472 if (Sin[s]=='\\') Value[v++]=Sin[++s];
473 else Value[v++]=Sin[s];
474 }
475 }
476 while(isspace(Sin[s])) s++; /* skip spaces */
477 return(Sin+s);
478 } /* GetFieldValue() */
479
480 /**
481 * \brief Read a line each time from one file
482 * \param[in] Fin The file stream
483 * \param[out] Line Save a line of content
484 * \param[in] MaxLine Max character count one time to read a line
485 *
486 * \return int - the character count of the line
487 *
488 */
ReadLine(FILE * Fin,char * Line,int MaxLine)489 int ReadLine(FILE *Fin, char *Line, int MaxLine)
490 {
491 int C;
492 int i;
493
494 memset(Line,'\0',MaxLine);
495 if (feof(Fin)) return(-1);
496 i=0;
497 C=fgetc(Fin);
498 if (C<0) return(-1);
499 while(!feof(Fin) && (C>=0) && (i<MaxLine))
500 {
501 if (C=='\n')
502 {
503 if (i > 0) return(i);
504 /* if it is a blank line, then ignore it. */
505 }
506 else
507 {
508 Line[i]=C;
509 i++;
510 }
511 C=fgetc(Fin);
512 }
513 return(i);
514 } /* ReadLine() */
515
516 /**
517 * \brief Here are some suggested options
518 *
519 * \param Name - the name of the executable, usually it is mimetype
520 */
Usage(char * Name)521 void Usage(char *Name)
522 {
523 printf("Usage: %s [options] [file [file [...]]\n",Name);
524 printf(" -h :: help (print this message), then exit.\n");
525 printf(" -i :: initialize the database, then exit.\n");
526 printf(" -v :: verbose (-vv = more verbose)\n");
527 printf(" -c :: Specify the directory for the system configuration.\n");
528 printf(" -C :: run from command line.\n");
529 printf(" -V :: print the version info, then exit.\n");
530 printf(" file :: if files are listed, display their mimetype.\n");
531 printf(" no file :: process data from the scheduler.\n");
532 } /* Usage() */
533