1 /*
2   Copyright 2021 Northern.tech AS
3 
4   This file is part of CFEngine 3 - written and maintained by Northern.tech AS.
5 
6   This program is free software; you can redistribute it and/or modify it
7   under the terms of the GNU General Public License as published by the
8   Free Software Foundation; version 3.
9 
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14 
15   You should have received a copy of the GNU General Public License
16   along with this program; if not, write to the Free Software
17   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
18 
19   To the extent this program is licensed as part of the Enterprise
20   versions of CFEngine, the applicable Commercial Open Source License
21   (COSL) may apply to this file if you as a licensee so wish it. See
22   included file COSL.txt.
23 */
24 
25 #include <cf3.defs.h>
26 
27 #include <files_names.h>
28 #include <vercmp_internal.h>
29 #include <rlist.h>
30 
31 static void ParsePackageVersion(char *version, Rlist **num, Rlist **sep);
32 
ComparePackageVersionsInternal(const char * v1,const char * v2,PackageVersionComparator cmp)33 VersionCmpResult ComparePackageVersionsInternal(const char *v1, const char *v2, PackageVersionComparator cmp)
34 {
35     Rlist *rp_pr, *rp_in;
36 
37     int result = true;
38     int break_loop = false;
39     int cmp_result;
40     VersionCmpResult version_matched = VERCMP_NO_MATCH;
41 
42     Rlist *numbers_pr = NULL, *separators_pr = NULL;
43     Rlist *numbers_in = NULL, *separators_in = NULL;
44 
45     ParsePackageVersion(CanonifyChar(v1, ','), &numbers_pr, &separators_pr);
46     ParsePackageVersion(CanonifyChar(v2, ','), &numbers_in, &separators_in);
47 
48 /* If the format of the version string doesn't match, we're already doomed */
49 
50     Log(LOG_LEVEL_VERBOSE, "Check for compatible versioning model in (%s,%s)", v1, v2);
51 
52     for (rp_pr = separators_pr, rp_in = separators_in; (rp_pr != NULL) && (rp_in != NULL);
53          rp_pr = rp_pr->next, rp_in = rp_in->next)
54     {
55         if (strcmp(RlistScalarValue(rp_pr), RlistScalarValue(rp_in)) != 0)
56         {
57             result = false;
58             break;
59         }
60 
61         if ((rp_pr->next == NULL) && (rp_in->next == NULL))
62         {
63             result = true;
64             break;
65         }
66     }
67 
68     if (result)
69     {
70         Log(LOG_LEVEL_VERBOSE, "Verified that versioning models are compatible");
71     }
72     else
73     {
74         Log(LOG_LEVEL_VERBOSE, "Versioning models for (%s,%s) were incompatible", v1, v2);
75         version_matched = VERCMP_ERROR;
76     }
77 
78     int version_equal = (strcmp(v2, v1) == 0);
79 
80     if (result)
81     {
82         for (rp_pr = numbers_pr, rp_in = numbers_in; (rp_pr != NULL) && (rp_in != NULL);
83              rp_pr = rp_pr->next, rp_in = rp_in->next)
84         {
85             cmp_result = strcmp(RlistScalarValue(rp_pr), RlistScalarValue(rp_in));
86 
87             switch (cmp)
88             {
89             case PACKAGE_VERSION_COMPARATOR_EQ:
90             case PACKAGE_VERSION_COMPARATOR_NONE:
91                 if (version_equal)
92                 {
93                     version_matched = VERCMP_MATCH;
94                 }
95                 break;
96             case PACKAGE_VERSION_COMPARATOR_NEQ:
97                 if (!version_equal)
98                 {
99                     version_matched = VERCMP_MATCH;
100                 }
101                 break;
102             case PACKAGE_VERSION_COMPARATOR_GT:
103                 if (cmp_result > 0)
104                 {
105                     version_matched = VERCMP_MATCH;
106                 }
107                 else if (cmp_result < 0)
108                 {
109                     break_loop = true;
110                 }
111                 break;
112             case PACKAGE_VERSION_COMPARATOR_LT:
113                 if (cmp_result < 0)
114                 {
115                     version_matched = VERCMP_MATCH;
116                 }
117                 else if (cmp_result > 0)
118                 {
119                     break_loop = true;
120                 }
121                 break;
122             case PACKAGE_VERSION_COMPARATOR_GE:
123                 if ((cmp_result > 0) || version_equal)
124                 {
125                     version_matched = VERCMP_MATCH;
126                 }
127                 else if (cmp_result < 0)
128                 {
129                     break_loop = true;
130                 }
131                 break;
132             case PACKAGE_VERSION_COMPARATOR_LE:
133                 if ((cmp_result < 0) || version_equal)
134                 {
135                     version_matched = VERCMP_MATCH;
136                 }
137                 else if (cmp_result > 0)
138                 {
139                     break_loop = true;
140                 }
141                 break;
142             default:
143                 break;
144             }
145 
146             if ((version_matched == VERCMP_MATCH) || break_loop)
147             {
148                 rp_pr = NULL;
149                 rp_in = NULL;
150                 break;
151             }
152         }
153 
154         if (rp_pr != NULL)
155         {
156             if ((cmp == PACKAGE_VERSION_COMPARATOR_GT) || (cmp == PACKAGE_VERSION_COMPARATOR_GE))
157             {
158                 version_matched = VERCMP_MATCH;
159             }
160         }
161         if (rp_in != NULL)
162         {
163             if ((cmp == PACKAGE_VERSION_COMPARATOR_LT) || (cmp == PACKAGE_VERSION_COMPARATOR_LE))
164             {
165                 version_matched = VERCMP_MATCH;
166             }
167         }
168     }
169 
170     RlistDestroy(numbers_pr);
171     RlistDestroy(numbers_in);
172     RlistDestroy(separators_pr);
173     RlistDestroy(separators_in);
174 
175     return version_matched;
176 }
177 
ParsePackageVersion(char * version,Rlist ** num,Rlist ** sep)178 static void ParsePackageVersion(char *version, Rlist **num, Rlist **sep)
179 {
180     char *sp, numeral[30], separator[2];
181 
182     if (version == NULL)
183     {
184         return;
185     }
186 
187     for (sp = version; *sp != '\0'; sp++)
188     {
189         memset(numeral, 0, 30);
190         memset(separator, 0, 2);
191 
192         /* Split in 2's complement */
193 
194         sscanf(sp, "%29[0-9a-zA-Z]", numeral);
195         sp += strlen(numeral);
196 
197         /* Append to end up with left->right (major->minor) comparison */
198 
199         RlistAppendScalar(num, numeral);
200 
201         if (*sp == '\0')
202         {
203             return;
204         }
205 
206         sscanf(sp, "%1[^0-9a-zA-Z]", separator);
207         RlistAppendScalar(sep, separator);
208     }
209 }
210