/[gnats]/gnats/gnats/edit.c
ViewVC logotype

Diff of /gnats/gnats/edit.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.60 by yngves, Mon Nov 25 13:58:33 2002 UTC revision 1.61 by hatzis, Thu Feb 24 19:21:12 2005 UTC
# Line 24  Software Foundation, 59 Temple Place - S Line 24  Software Foundation, 59 Temple Place - S
24  #include "query.h"  #include "query.h"
25  #include "mail.h"  #include "mail.h"
26    
 static int applyChangeActions (PR *pr, PR *oldPR, FieldIndex field,  
                                ChangeActions actions, ErrorDesc *err,  
                                FormatNamedParameter *params);  
   
 static int processFieldChange (PR *pr, PR *oldPR, FieldIndex field,  
                                ErrorDesc *err,  
                                const char *editUserEmailAddr,  
                                const char *oldValue, const char *newValue);  
   
27  /* Euuugh.  There's gotta be a better way to keep this around.  */  /* Euuugh.  There's gotta be a better way to keep this around.  */
28  static char *newAuditTrailEntries = NULL;  static char *newAuditTrailEntries = NULL;
29    
30  static void sendAuditMail (PR *pr, PR *oldPR, const char *editUserEmailAddr,  static void
31                             const char *newAuditTrailEntries,  sendAuditMail (PR *pr, PR *oldPR, const char *editUserEmailAddr,
32                             ErrorDesc *err);                 const char *newAuditTrailEntries, ErrorDesc *err)
33    {
34      FormatNamedParameter *parms = NULL;
35      const DatabaseInfo database = pr->database;
36    
37  int    parms = allocateNamedParameter ("$EditUserEmailAddr", editUserEmailAddr,
38  replace_pr (const DatabaseInfo database, FILE *fp,                                    parms);
39              const char *editUserEmailAddr, ErrorDesc *err)    parms = allocateNamedParameter ("$NewAuditTrail", newAuditTrailEntries,
40                                      parms);
41    
42      if (strcmp (field_value (pr, RESPONSIBLE (database)),
43                  field_value (oldPR, RESPONSIBLE (database))) != 0)
44        {
45          parms
46            = allocateNamedParameter ("$OldResponsible",
47                                      field_value (oldPR, RESPONSIBLE (database)),
48                                      parms);
49        }
50    
51      composeMailMessage (pr, oldPR, "audit-mail", parms, NULL, err);
52      freeFormatParameterList (parms);
53    }
54    
55    /* Add an entry to the AUDIT_TRAIL field. PARAMS are the format
56       parameters used when composing the entry.  FMT is the format of the
57       audit-trail entry to add.  */
58    
59    static int
60    addAuditTrailEnt (PR *pr, QueryFormat *fmt,
61                      FormatNamedParameter *params, ErrorDesc *err)
62  {  {
63    PR *pr = allocPR (database);    char *newAuditString = NULL;
64      char *finalAuditString;
65      const char *t;
66      char *currDate = get_curr_date ();
67    
68    /* Read the message header in.  */    if (fmt == NULL)
   if (read_header (pr, fp) < 0)  
69      {      {
70        setError (err, CODE_EOF_PR, "Couldn't read PR header");        fmt = getAuditTrailFormat (pr->database);
       return 0;  
71      }      }
72    
73    read_pr (pr, fp, 0);    /* Format the new audit entry. */
74      process_format (NULL, &newAuditString, pr, NULL, fmt, "\n", params);
75    
76    return rewrite_pr (pr, editUserEmailAddr, err);    /* Squirrel it away, because we'll need to mail it later.  */
77      append_string (&newAuditTrailEntries, newAuditString);
78    
79      /* Now append the formatted string to the audit-trail field.  */
80      t = field_value (pr, AUDIT_TRAIL (pr->database));
81      if (t == NULL)
82        {
83          t = "";
84        }
85      finalAuditString = xstrdup (t);
86      append_string (&finalAuditString, newAuditString);
87      set_field (pr, AUDIT_TRAIL (pr->database), finalAuditString, err);
88    
89      free (finalAuditString);
90      free (newAuditString);
91      free (currDate);
92    
93      return 0;
94    }
95    
96    static int
97    applyChangeAction (ChangeActions action, PR *pr, PR *oldPR, FieldIndex field,
98                       ErrorDesc *err, FormatNamedParameter *params)
99    {
100      FieldEdit *fieldEdits = action->edits;
101      FieldList fields = action->requiredFields;
102    
103      while (fields != NULL)
104        {
105          const char *fldval = get_field_value (pr, oldPR, fields->ent, NULL, NULL);
106          if (value_is_empty (fldval))
107            {
108              setError (err, CODE_INVALID_PR_CONTENTS,
109                        "Required field %s missing from PR %s.",
110                        complexFieldIndexToString (fields->ent),
111                        field_value (pr, NUMBER (pr->database)));
112              return 1;
113            }
114          fields = fields->next;
115        }
116    
117      if (action->requireChangeReason && field_change_reason (pr, field) == NULL)
118        {
119          setError (err, CODE_INVALID_PR_CONTENTS,
120                    "Edit of field %s requires a change reason.",
121                    fieldDefForIndex (field)->name);
122          return 1;
123        }
124    
125      while (fieldEdits != NULL)
126        {
127          if (applyFieldEdit (pr, fieldEdits, err, params) != 0)
128            {
129              return 1;
130            }
131          fieldEdits = fieldEdits->next;
132        }
133      return 0;
134  }  }
135    
136  static int  static int
# Line 88  addAuditEntryP (const DatabaseInfo datab Line 163  addAuditEntryP (const DatabaseInfo datab
163  }  }
164    
165  static int  static int
166    applyChangeActions (PR *pr, PR *oldPR, FieldIndex field,
167                        ChangeActions actions, ErrorDesc *err,
168                        FormatNamedParameter *params)
169    {
170      {
171        ChangeActions actionList = actions;
172    
173        while (actionList != NULL)
174          {
175            if (actionList->expr == NULL
176                || pr_matches_expr (pr, oldPR, actionList->expr, params))
177              {
178                if (applyChangeAction (actionList, pr, oldPR, field, err, params))
179                  {
180                    return 1;
181                  }
182              }
183            actionList = actionList->next;
184          }
185      }
186    
187      if (field != InvalidFieldIndex && addAuditEntryP (pr->database,
188                                                        field, actions))
189        {
190          ChangeActions action = actions;
191          while (actions != NULL)
192            {
193              if (actions->addAuditTrail)
194                {
195                  break;
196                }
197              actions = actions->next;
198            }
199    
200          if (action != NULL)
201            {
202              addAuditTrailEnt (pr, action->auditTrailFormat, params, err);
203            }
204          else
205            {
206              addAuditTrailEnt (pr, NULL, params, err);
207            }
208        }
209    
210      return 0;
211    }
212    
213    static int
214    processFieldChange (PR *pr, PR *oldPR, FieldIndex field,
215                        ErrorDesc *err, const char *editUserEmailAddr,
216                        const char *oldValue, const char *newValue)
217    {
218      ChangeActions actions;
219      FormatNamedParameter *params = NULL;
220      int res;
221    
222      if (oldValue == NULL)
223        {
224          oldValue = "";
225        }
226      if (newValue == NULL)
227        {
228          newValue = "";
229        }
230    
231      if (fieldDefForIndex (field)->readonly)
232        {
233          setError (err, CODE_READONLY_FIELD,
234                    newBadFieldEntry (field, NULL, NULL),
235                    "Field %s is read-only: `%s'->`%s'",
236                    fieldDefForIndex (field)->name,
237                    oldValue,
238                    newValue);
239          return 1;
240        }
241    
242      {
243        char *currDate = get_curr_date ();
244        params = allocateNamedParameter ("$CurrentDate", currDate, params);
245        free (currDate);
246      }
247    
248      params = allocateNamedParameter ("$FieldName",
249                                       fieldDefForIndex (field)->name, params);
250      params = allocateNamedParameter ("$OldValue", oldValue, params);
251      params = allocateNamedParameter ("$NewValue", newValue, params);
252      params = allocateNamedParameter ("$EditUserEmailAddr", editUserEmailAddr,
253                                       params);
254      {
255        const char *reason = field_change_reason (pr, field);
256        if (reason == NULL)
257          {
258            reason = "";
259          }
260        params = allocateNamedParameter ("$ChangeReason", reason , params);
261      }
262    
263      actions = fieldDefForIndex (field)->changeActions;
264      res = applyChangeActions (pr, oldPR, field, actions, err, params);
265      freeFormatParameterList (params);
266      return res;
267    }
268    
269    static int
270  processPRChanges (const char *editUserEmailAddr, PR *old_pr, PR *new_pr,  processPRChanges (const char *editUserEmailAddr, PR *old_pr, PR *new_pr,
271                    ErrorDesc *err)                    ErrorDesc *err)
272  {  {
# Line 152  processPRChanges (const char *editUserEm Line 331  processPRChanges (const char *editUserEm
331            if (fieldDefForIndex (field)->readonly)            if (fieldDefForIndex (field)->readonly)
332              {              {
333                fieldsChanged[x] = 0;                fieldsChanged[x] = 0;
334                set_field (new_pr, field, old_value, err);                if (old_value == NULL)
335                    {
336                      unsetField (new_pr, field);
337                    }
338                  else
339                    {
340                      set_field (new_pr, field, old_value, err);
341                    }
342              }              }
343            else            else
344              {              {
# Line 211  processPRChanges (const char *editUserEm Line 397  processPRChanges (const char *editUserEm
397  /* Replace an existing PR with the same PR number as NEW_PR.  /* Replace an existing PR with the same PR number as NEW_PR.
398     EDITUSEREMAILADDR is the email address of the user that is performing the     EDITUSEREMAILADDR is the email address of the user that is performing the
399     edit.  */     edit.  */
400  int  static int
401  rewrite_pr (PR *new_pr, const char *editUserEmailAddr, ErrorDesc *err)  rewrite_pr (PR *pr, PR *new_pr, const char *editUserEmailAddr, ErrorDesc *err)
402  {  {
403    const DatabaseInfo database = new_pr->database;    const DatabaseInfo database = new_pr->database;
404    FILE *prfile;    FILE *prfile;
405    
406    char *path =      NULL;    char *new_path = NULL;
407    char *old_path =  NULL;    char *old_path = NULL;
408    char *bkup_path = NULL;    char *bkup_path = NULL;
409    
   PR *old_pr = NULL;  
   
410    int res = 1;    int res = 1;
411    
412    newAuditTrailEntries = NULL;    newAuditTrailEntries = NULL;
413    
   if (field_value (new_pr, NUMBER (database)) == NULL)  
     {  
       free_pr (new_pr);  
       setError (err, CODE_INVALID_PR_CONTENTS,  
                 "Missing PR number from new PR contents");  
       return 0;  
     }  
   
414    if (field_value (new_pr, CATEGORY (database)) == NULL)    if (field_value (new_pr, CATEGORY (database)) == NULL)
415      {      {
       free_pr (new_pr);  
416        setError (err, CODE_INVALID_PR_CONTENTS,        setError (err, CODE_INVALID_PR_CONTENTS,
417                  "Missing category from new PR contents");                  "Missing category from new PR contents");
418        return 0;        return 0;
419      }      }
420    
   if (! prExists (new_pr->database, field_value (new_pr, NUMBER (database)),  
                   err))  
     {  
       free_pr (new_pr);  
       return 0;  
     }  
   
421    if (! isPrLocked (new_pr->database, field_value (new_pr, NUMBER (database))))    if (! isPrLocked (new_pr->database, field_value (new_pr, NUMBER (database))))
422      {      {
423        setError (err, CODE_PR_NOT_LOCKED, "PR %s is not locked",        setError (err, CODE_PR_NOT_LOCKED, "PR %s is not locked",
424                  field_value (new_pr, NUMBER (database)));                  field_value (new_pr, NUMBER (database)));
       free_pr (new_pr);  
425        return 0;        return 0;
426      }      }
427    
428    if (! check_pr (new_pr, err, 0))    if (processPRChanges (editUserEmailAddr, pr, new_pr, err) != 0)
     {  
       return 0;  
     }  
   
   old_pr = replaceCurrentPRInIndex (new_pr);  
   
   if (old_pr == NULL)  
429      {      {
       setError (err, CODE_NO_INDEX, "Unable to locate existing PR %s",  
                 field_value (new_pr, NUMBER (database)));  
       free_pr (new_pr);  
       return 0;  
     }  
   
   old_path = gen_pr_path (old_pr);  
   
   /* set this to be the file to be saved. now called .old. */  
   asprintf (&bkup_path, "%s.old", old_path);  
   
   if (processPRChanges (editUserEmailAddr, old_pr, new_pr, err) != 0)  
     {  
       clearPRChain (database);  
       free_pr (old_pr);  
       free (bkup_path);  
       free (old_path);  
430        return 0;        return 0;
431      }      }
432    
433    /* check to see if the category changes, and if so, write the    /* check to see if the category changes, and if so, write the
434       new file out, and unlink the old one.  */       new file out, and unlink the old one.  */
435    if (strcmp (field_value (old_pr, CATEGORY (database)),    if (strcmp (field_value (pr, CATEGORY (database)),
436                field_value (new_pr, CATEGORY (database))) != 0)                field_value (new_pr, CATEGORY (database))) != 0)
437      {      {
438        char *dirPath;        char *dirPath;
# Line 309  rewrite_pr (PR *new_pr, const char *edit Line 452  rewrite_pr (PR *new_pr, const char *edit
452                              "Error creating directory for category %s: %s",                              "Error creating directory for category %s: %s",
453                              field_value (new_pr, CATEGORY (database)),                              field_value (new_pr, CATEGORY (database)),
454                              strerror (errno));                              strerror (errno));
                   clearPRChain (database);  
                   free_pr (old_pr);  
                   free (bkup_path);  
                   free (old_path);  
455                    free (dirPath);                    free (dirPath);
456                    return 0;                    return 0;
457                  }                  }
# Line 322  rewrite_pr (PR *new_pr, const char *edit Line 461  rewrite_pr (PR *new_pr, const char *edit
461                setError (err, CODE_FILE_ERROR,                setError (err, CODE_FILE_ERROR,
462                          "Directory for category %s does not exist",                          "Directory for category %s does not exist",
463                          field_value (new_pr, CATEGORY (database)));                          field_value (new_pr, CATEGORY (database)));
               clearPRChain (database);  
               free_pr (old_pr);  
               free (bkup_path);  
               free (old_path);  
464                free (dirPath);                free (dirPath);
465                return 0;                return 0;
466              }              }
467          }          }
468        free (dirPath);        free (dirPath);
       path = gen_pr_path (new_pr);  
     }  
   else  
     {  
       path = gen_pr_path (old_pr);  
469      }      }
470    
471      /* backup the current PR file */
472      old_path = gen_pr_path (pr);
473      asprintf (&bkup_path, "%s.old", old_path);
474    if (rename (old_path, bkup_path) < 0)    if (rename (old_path, bkup_path) < 0)
475      {      {
476        if (errno != EXDEV)        if (errno != EXDEV)
477          {          {
478            setError (err, CODE_FILE_ERROR, "Could not rename %s: %s",            setError (err, CODE_FILE_ERROR, "Could not rename %s: %s",
479                      old_path, strerror (errno));                      old_path, strerror (errno));
           clearPRChain (database);  
           free_pr (old_pr);  
480            free (bkup_path);            free (bkup_path);
481            free (old_path);            free (old_path);
           free (path);  
482            return 0;            return 0;
483          }          }
484        if (copy_file (old_path, bkup_path) != 0)        if (copy_file (old_path, bkup_path) != 0)
485          {          {
486            setError (err, CODE_FILE_ERROR, "Could not copy %s to %s: %s",            setError (err, CODE_FILE_ERROR, "Could not copy %s to %s: %s",
487                      old_path, bkup_path, strerror (errno));                      old_path, bkup_path, strerror (errno));
           clearPRChain (database);  
           free_pr (old_pr);  
488            free (bkup_path);            free (bkup_path);
489            free (old_path);            free (old_path);
           free (path);  
490            return 0;            return 0;
491          }          }
492    
# Line 369  rewrite_pr (PR *new_pr, const char *edit Line 496  rewrite_pr (PR *new_pr, const char *edit
496      }      }
497    
498    /* Now build the file.  */    /* Now build the file.  */
499    prfile = fopen (path, "w+");    new_path = gen_pr_path (new_pr);
500      prfile = fopen (new_path, "w+");
501    if (prfile == (FILE *) NULL)    if (prfile == (FILE *) NULL)
502      {      {
503        setError (err, CODE_FILE_ERROR, "Cannot write the PR to %s: %s",        setError (err, CODE_FILE_ERROR, "Cannot write the PR to %s: %s",
504                  path, strerror (errno));                  new_path, strerror (errno));
       clearPRChain (database);  
       free_pr (old_pr);  
505        free (bkup_path);        free (bkup_path);
506        free (old_path);        free (old_path);
507        free (path);        free (new_path);
508        return 0;        return 0;
509      }      }
510    
# Line 394  rewrite_pr (PR *new_pr, const char *edit Line 520  rewrite_pr (PR *new_pr, const char *edit
520    if (fclose (prfile) == EOF)    if (fclose (prfile) == EOF)
521      {      {
522        setError (err, CODE_FILE_ERROR, "Error writing out PR %s: %s",        setError (err, CODE_FILE_ERROR, "Error writing out PR %s: %s",
523                  path, strerror (errno));                  new_path, strerror (errno));
       clearPRChain (database);  
       free_pr (old_pr);  
524        free (bkup_path);        free (bkup_path);
525        free (old_path);        free (old_path);
526        free (path);        free (new_path);
527        return 0;        return 0;
528      }      }
529    
530    unlink (bkup_path);    unlink (bkup_path);
531    
532    /* unlink the old file, if it is in another category dir. */    /* unlink the old file, if it is in another category dir. */
533    if (strcmp (field_value (old_pr, CATEGORY (database)),    if (strcmp (field_value (pr, CATEGORY (database)),
534                field_value (new_pr, CATEGORY (database))) != 0)                field_value (new_pr, CATEGORY (database))) != 0)
535      {      {
536        unlink (old_path);        unlink (old_path);
537      }      }
538    
539      free (bkup_path);
540      free (old_path);
541      free (new_path);
542    
543    /* write out the new index.  */    /* write out the new index.  */
544      replaceCurrentPRInIndex (pr, new_pr, err);
545    if (writeIndex (database, err) != 0)    if (writeIndex (database, err) != 0)
546      {      {
547        res = 0;        res = 0;
# Line 421  rewrite_pr (PR *new_pr, const char *edit Line 550  rewrite_pr (PR *new_pr, const char *edit
550    /* Send mail about the edit.  */    /* Send mail about the edit.  */
551    if (newAuditTrailEntries != NULL)    if (newAuditTrailEntries != NULL)
552      {      {
553        sendAuditMail (new_pr, old_pr, editUserEmailAddr, newAuditTrailEntries,        sendAuditMail (new_pr, pr, editUserEmailAddr, newAuditTrailEntries,
554                       err);                       err);
555        free (newAuditTrailEntries);        free (newAuditTrailEntries);
556        newAuditTrailEntries = NULL;        newAuditTrailEntries = NULL;
557      }      }
558    
559    free_pr (old_pr);    return res;
560    free (bkup_path);  }
561    free (old_path);  
562    free (path);  
563    int
564    replace_pr (const DatabaseInfo database, FILE *fp,
565                const char *editUserEmailAddr, ErrorDesc *err)
566    {
567      int res;
568      const char *prnum = NULL;
569      PR *curr_pr;
570      PR *new_pr = allocPR (database);
571    
572      /* Read the message header in.  */
573      if (read_header (new_pr, fp) < 0)
574        {
575          setError (err, CODE_EOF_PR, "Couldn't read PR header");
576          return 0;
577        }
578    
579      read_pr (new_pr, fp, 0);
580    
581      prnum = field_value (new_pr, NUMBER (database));
582      if (prnum == NULL)
583        {
584          setError (err, CODE_INVALID_PR_CONTENTS,
585                    "Missing PR number from new PR contents");
586          return 0;
587        }
588    
589      if (! prExists (database, prnum, err))
590        {
591          return 0;
592        }
593    
594      curr_pr = readPRWithNum (database, prnum, 0, err);
595      if (curr_pr == NULL)
596        {
597          setError (err, CODE_NO_INDEX,
598                    "Unable to locate existing PR %s", prnum);
599          return 0;
600        }
601    
602      if (! check_pr (new_pr, err, 0))
603        {
604          return 0;
605        }
606    
607      res = rewrite_pr (curr_pr, new_pr, editUserEmailAddr, err);
608    
609      free_pr (curr_pr);
610      free_pr (new_pr);
611    
612    return res;    return res;
613  }  }
# Line 815  edit_field (const DatabaseInfo database, Line 992  edit_field (const DatabaseInfo database,
992    char pid[32];    char pid[32];
993    const char *oldfield;    const char *oldfield;
994    int res;    int res;
995    PR *pr;    PR *pr, *new_pr;
996    
997    if (! prExists (database, prnum, err))    if (! prExists (database, prnum, err))
998      {      {
999        free (newcontents);        free (newcontents);
1000          if (changeReason != NULL)
1001            {
1002              free (changeReason);
1003            }
1004        return 0;        return 0;
1005      }      }
1006    if (fieldIndex == InvalidFieldIndex)    if (fieldIndex == InvalidFieldIndex)
1007      {      {
1008        setError (err, CODE_INVALID_FIELD_NAME, "Invalid field name");        setError (err, CODE_INVALID_FIELD_NAME, "Invalid field name");
1009        free (newcontents);        free (newcontents);
1010          if (changeReason != NULL)
1011            {
1012              free (changeReason);
1013            }
1014        return 0;        return 0;
1015      }      }
1016    if (requiresChangeReason (fieldDefForIndex (fieldIndex))    if (requiresChangeReason (fieldDefForIndex (fieldIndex))
# Line 837  edit_field (const DatabaseInfo database, Line 1022  edit_field (const DatabaseInfo database,
1022        free (newcontents);        free (newcontents);
1023        return 0;        return 0;
1024      }      }
1025    
1026    sprintf (pid, "%d", (int) getpid ());    sprintf (pid, "%d", (int) getpid ());
1027    if (lock_pr (database, prnum, "edit_field", pid, err) == 0)    if (lock_pr (database, prnum, "edit_field", pid, err) == 0)
1028      {      {
1029        free (newcontents);        free (newcontents);
1030          if (changeReason != NULL)
1031            {
1032              free (changeReason);
1033            }
1034        return 0;        return 0;
1035      }      }
1036    
# Line 849  edit_field (const DatabaseInfo database, Line 1039  edit_field (const DatabaseInfo database,
1039      {      {
1040        unlock_pr (database, prnum, err);        unlock_pr (database, prnum, err);
1041        setError (err, CODE_UNREADABLE_PR, "Error reading PR %s.", prnum);        setError (err, CODE_UNREADABLE_PR, "Error reading PR %s.", prnum);
       free_pr (pr);  
1042        free (newcontents);        free (newcontents);
1043        if (changeReason != NULL)        if (changeReason != NULL)
1044          {          {
# Line 876  edit_field (const DatabaseInfo database, Line 1065  edit_field (const DatabaseInfo database,
1065        newcontents = totalVal;        newcontents = totalVal;
1066      }      }
1067    
1068      /* create a copy of the currently active pr so that we can modify it and
1069         do subsequent validation allowing for us to bail out cleanly */
1070      new_pr = allocPR (database);
1071      set_field (new_pr, NUMBER (database), prnum, err);
1072      set_field (new_pr, CATEGORY (database), field_value (pr, CATEGORY (database)),
1073                 err);
1074      fillInPR (new_pr, err);
1075    
1076    /* set_field () verifies that the value is valid before doing the    /* set_field () verifies that the value is valid before doing the
1077       set.  */       set.  */
1078    if (set_field (pr, fieldIndex, newcontents, err) == 0)    /* set_field returns a boolean!!! */
1079      if (set_field (new_pr, fieldIndex, newcontents, err) == 0)
1080      {      {
1081        res = 0;        res = 0;
       free_pr (pr);  
1082      }      }
1083    else    else
1084      {      {
1085        if (changeReason != NULL)        if (changeReason != NULL)
1086          {          {
1087            setFieldChangeReason (pr, fieldIndex, changeReason);            setFieldChangeReason (new_pr, fieldIndex, changeReason);
1088            free (changeReason);            free (changeReason);
1089            changeReason = NULL;            changeReason = NULL;
1090          }          }
1091    
1092        res = rewrite_pr (pr, editUserEmailAddr, err);        res = rewrite_pr (pr, new_pr, editUserEmailAddr, err);
     }  
   
   if (unlock_pr (database, prnum, err) == 0)  
     {  
       res = 0;  
1093      }      }
1094    
1095      free_pr (pr);
1096      free_pr (new_pr);
1097    free (newcontents);    free (newcontents);
1098    if (changeReason != NULL)    if (changeReason != NULL)
1099      {      {
1100        free (changeReason);        free (changeReason);
1101        changeReason = NULL;        changeReason = NULL;
1102      }      }
1103    
1104      if (unlock_pr (database, prnum, err) == 0)
1105        {
1106          res = 0;
1107        }
1108    
1109    return res;    return res;
1110  }  }
1111    
# Line 1029  applyFieldEdit (PR *pr, FieldEdit *edit, Line 1229  applyFieldEdit (PR *pr, FieldEdit *edit,
1229    return res;    return res;
1230  }  }
1231    
 /* Add an entry to the AUDIT_TRAIL field. PARAMS are the format  
    parameters used when composing the entry.  FMT is the format of the  
    audit-trail entry to add.  */  
   
 static int  
 addAuditTrailEnt (PR *pr, QueryFormat *fmt,  
                   FormatNamedParameter *params, ErrorDesc *err)  
 {  
   char *newAuditString = NULL;  
   char *finalAuditString;  
   const char *t;  
   char *currDate = get_curr_date ();  
   
   if (fmt == NULL)  
     {  
       fmt = getAuditTrailFormat (pr->database);  
     }  
   
   /* Format the new audit entry. */  
   process_format (NULL, &newAuditString, pr, NULL, fmt, "\n", params);  
   
   /* Squirrel it away, because we'll need to mail it later.  */  
   append_string (&newAuditTrailEntries, newAuditString);  
   
   /* Now append the formatted string to the audit-trail field.  */  
   t = field_value (pr, AUDIT_TRAIL (pr->database));  
   if (t == NULL)  
     {  
       t = "";  
     }  
   finalAuditString = xstrdup (t);  
   append_string (&finalAuditString, newAuditString);  
   set_field (pr, AUDIT_TRAIL (pr->database), finalAuditString, err);  
   
   free (finalAuditString);  
   free (newAuditString);  
   free (currDate);  
   
   return 0;  
 }  
   
 static void  
 sendAuditMail (PR *pr, PR *oldPR, const char *editUserEmailAddr,  
                const char *newAuditTrailEntries, ErrorDesc *err)  
 {  
   FormatNamedParameter *parms = NULL;  
   const DatabaseInfo database = pr->database;  
   
   parms = allocateNamedParameter ("$EditUserEmailAddr", editUserEmailAddr,  
                                   parms);  
   parms = allocateNamedParameter ("$NewAuditTrail", newAuditTrailEntries,  
                                   parms);  
   
   if (strcmp (field_value (pr, RESPONSIBLE (database)),  
               field_value (oldPR, RESPONSIBLE (database))) != 0)  
     {  
       parms  
         = allocateNamedParameter ("$OldResponsible",  
                                   field_value (oldPR, RESPONSIBLE (database)),  
                                   parms);  
     }  
   
   composeMailMessage (pr, oldPR, "audit-mail", parms, NULL, err);  
   freeFormatParameterList (parms);  
 }  
   
 static int  
 applyChangeAction (ChangeActions action, PR *pr, PR *oldPR, FieldIndex field,  
                    ErrorDesc *err, FormatNamedParameter *params)  
 {  
   FieldEdit *fieldEdits = action->edits;  
   FieldList fields = action->requiredFields;  
   
   while (fields != NULL)  
     {  
       const char *fldval = get_field_value (pr, oldPR, fields->ent, NULL, NULL);  
       if (value_is_empty (fldval))  
         {  
           setError (err, CODE_INVALID_PR_CONTENTS,  
                     "Required field %s missing from PR %s.",  
                     complexFieldIndexToString (fields->ent),  
                     field_value (pr, NUMBER (pr->database)));  
           return 1;  
         }  
       fields = fields->next;  
     }  
   
   if (action->requireChangeReason && field_change_reason (pr, field) == NULL)  
     {  
       setError (err, CODE_INVALID_PR_CONTENTS,  
                 "Edit of field %s requires a change reason.",  
                 fieldDefForIndex (field)->name);  
       return 1;  
     }  
   
   while (fieldEdits != NULL)  
     {  
       if (applyFieldEdit (pr, fieldEdits, err, params) != 0)  
         {  
           return 1;  
         }  
       fieldEdits = fieldEdits->next;  
     }  
   return 0;  
 }  
   
 static int  
 applyChangeActions (PR *pr, PR *oldPR, FieldIndex field,  
                     ChangeActions actions, ErrorDesc *err,  
                     FormatNamedParameter *params)  
 {  
   {  
     ChangeActions actionList = actions;  
   
     while (actionList != NULL)  
       {  
         if (actionList->expr == NULL  
             || pr_matches_expr (pr, oldPR, actionList->expr, params))  
           {  
             if (applyChangeAction (actionList, pr, oldPR, field, err, params))  
               {  
                 return 1;  
               }  
           }  
         actionList = actionList->next;  
       }  
   }  
   
   if (field != InvalidFieldIndex && addAuditEntryP (pr->database,  
                                                     field, actions))  
     {  
       ChangeActions action = actions;  
       while (actions != NULL)  
         {  
           if (actions->addAuditTrail)  
             {  
               break;  
             }  
           actions = actions->next;  
         }  
   
       if (action != NULL)  
         {  
           addAuditTrailEnt (pr, action->auditTrailFormat, params, err);  
         }  
       else  
         {  
           addAuditTrailEnt (pr, NULL, params, err);  
         }  
     }  
   
   return 0;  
 }  
   
 static int  
 processFieldChange (PR *pr, PR *oldPR, FieldIndex field,  
                     ErrorDesc *err, const char *editUserEmailAddr,  
                     const char *oldValue, const char *newValue)  
 {  
   ChangeActions actions;  
   FormatNamedParameter *params = NULL;  
   int res;  
   
   if (oldValue == NULL)  
     {  
       oldValue = "";  
     }  
   if (newValue == NULL)  
     {  
       newValue = "";  
     }  
   
   if (fieldDefForIndex (field)->readonly)  
     {  
       setError (err, CODE_READONLY_FIELD,  
                 newBadFieldEntry (field, NULL, NULL),  
                 "Field %s is read-only: `%s'->`%s'",  
                 fieldDefForIndex (field)->name,  
                 oldValue,  
                 newValue);  
       return 1;  
     }  
   
   {  
     char *currDate = get_curr_date ();  
     params = allocateNamedParameter ("$CurrentDate", currDate, params);  
     free (currDate);  
   }  
   
   params = allocateNamedParameter ("$FieldName",  
                                    fieldDefForIndex (field)->name, params);  
   params = allocateNamedParameter ("$OldValue", oldValue, params);  
   params = allocateNamedParameter ("$NewValue", newValue, params);  
   params = allocateNamedParameter ("$EditUserEmailAddr", editUserEmailAddr,  
                                    params);  
   {  
     const char *reason = field_change_reason (pr, field);  
     if (reason == NULL)  
       {  
         reason = "";  
       }  
     params = allocateNamedParameter ("$ChangeReason", reason , params);  
   }  
   
   actions = fieldDefForIndex (field)->changeActions;  
   res = applyChangeActions (pr, oldPR, field, actions, err, params);  
   freeFormatParameterList (params);  
   return res;  
 }  
   
1232  /* Delete PR PRNUM.  */  /* Delete PR PRNUM.  */
1233  int  int
1234  deletePR (const DatabaseInfo database, const char *prnum,  deletePR (const DatabaseInfo database, const char *prnum,
1235            const char *editUserEmailAddr, ErrorDesc *err)            const char *editUserEmailAddr, ErrorDesc *err)
1236  {  {
   char *path = lookup_pr_path (database, prnum, err);  
1237    char pid[32];    char pid[32];
1238    FormatNamedParameter *parms = NULL;    FormatNamedParameter *parms = NULL;
1239      short delete_status;
   if (path == NULL)  
     {  
       return -1;  
     }  
1240    
1241    if (client_lock_gnats (database, err))    if (client_lock_gnats (database, err))
1242      {      {
       free (path);  
1243        return -4;        return -4;
1244      }      }
1245    
1246    if (lock_pr (database, prnum, GNATS_USER, pid, err) == 0)    if (lock_pr (database, prnum, GNATS_USER, pid, err) == 0)
1247      {      {
1248        client_unlock_gnats ();        client_unlock_gnats ();
       free (path);  
1249        return -4;        return -4;
1250      }      }
1251    
1252    if (removePRFromIndex (database, prnum, err))    if ((delete_status = pr_delete (database, prnum, err)) < 0)
     {  
       unlock_pr (database, prnum, err);  
       client_unlock_gnats ();  
       free (path);  
       return -5;  
     }  
     
   if (unlink (path))  
1253      {      {
       setError (err, CODE_FILE_ERROR, "Unable to unlink file %s\n", path);  
1254        unlock_pr (database, prnum, err);        unlock_pr (database, prnum, err);
1255        client_unlock_gnats ();        client_unlock_gnats ();
1256        free (path);        return delete_status;
       return -6;  
1257      }      }
1258    
   free (path);  
   
1259    if (unlock_pr (database, prnum, err) == 0)    if (unlock_pr (database, prnum, err) == 0)
1260      {      {
1261        client_unlock_gnats ();        client_unlock_gnats ();

Legend:
Removed from v.1.60  
changed lines
  Added in v.1.61

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26