wbemprox: Fix returned variant type and flavor for system properties.
[wine] / dlls / wbemprox / query.c
1 /*
2  * Copyright 2012 Hans Leidekker for CodeWeavers
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18
19 #define COBJMACROS
20
21 #include "config.h"
22 #include <stdarg.h>
23
24 #include "windef.h"
25 #include "winbase.h"
26 #include "wbemcli.h"
27
28 #include "wine/debug.h"
29 #include "wbemprox_private.h"
30
31 WINE_DEFAULT_DEBUG_CHANNEL(wbemprox);
32
33 static HRESULT get_column_index( const struct table *table, const WCHAR *name, UINT *column )
34 {
35     UINT i;
36     for (i = 0; i < table->num_cols; i++)
37     {
38         if (!strcmpiW( table->columns[i].name, name ))
39         {
40             *column = i;
41             return S_OK;
42         }
43     }
44     return WBEM_E_INVALID_QUERY;
45 }
46
47 static UINT get_column_size( const struct table *table, UINT column )
48 {
49     if (table->columns[column].type & CIM_FLAG_ARRAY) return sizeof(void *);
50
51     switch (table->columns[column].type & COL_TYPE_MASK)
52     {
53     case CIM_SINT16:
54     case CIM_UINT16:
55         return sizeof(INT16);
56     case CIM_SINT32:
57     case CIM_UINT32:
58         return sizeof(INT32);
59     case CIM_SINT64:
60     case CIM_UINT64:
61         return sizeof(INT64);
62     case CIM_DATETIME:
63     case CIM_STRING:
64         return sizeof(WCHAR *);
65     default:
66         ERR("unkown column type %u\n", table->columns[column].type & COL_TYPE_MASK);
67         break;
68     }
69     return sizeof(INT32);
70 }
71
72 static UINT get_column_offset( const struct table *table, UINT column )
73 {
74     UINT i, offset = 0;
75     for (i = 0; i < column; i++) offset += get_column_size( table, i );
76     return offset;
77 }
78
79 static UINT get_row_size( const struct table *table )
80 {
81     return get_column_offset( table, table->num_cols - 1 ) + get_column_size( table, table->num_cols - 1 );
82 }
83
84 static HRESULT get_value( const struct table *table, UINT row, UINT column, LONGLONG *val )
85 {
86     UINT col_offset, row_size;
87     const BYTE *ptr;
88
89     col_offset = get_column_offset( table, column );
90     row_size = get_row_size( table );
91     ptr = table->data + row * row_size + col_offset;
92
93     if (table->columns[column].type & CIM_FLAG_ARRAY)
94     {
95         *val = (LONGLONG)(INT_PTR)*(const void **)ptr;
96         return S_OK;
97     }
98     switch (table->columns[column].type & COL_TYPE_MASK)
99     {
100     case CIM_DATETIME:
101     case CIM_STRING:
102         *val = (LONGLONG)(INT_PTR)*(const WCHAR **)ptr;
103         break;
104     case CIM_SINT16:
105         *val = *(const INT16 *)ptr;
106         break;
107     case CIM_UINT16:
108         *val = *(const UINT16 *)ptr;
109         break;
110     case CIM_SINT32:
111         *val = *(const INT32 *)ptr;
112         break;
113     case CIM_UINT32:
114         *val = *(const UINT32 *)ptr;
115         break;
116     case CIM_SINT64:
117         *val = *(const INT64 *)ptr;
118         break;
119     case CIM_UINT64:
120         *val = *(const UINT64 *)ptr;
121         break;
122     default:
123         ERR("invalid column type %u\n", table->columns[column].type & COL_TYPE_MASK);
124         *val = 0;
125         break;
126     }
127     return S_OK;
128 }
129
130 static BSTR get_value_bstr( const struct table *table, UINT row, UINT column )
131 {
132     static const WCHAR fmt_signedW[] = {'%','d',0};
133     static const WCHAR fmt_unsignedW[] = {'%','u',0};
134     static const WCHAR fmt_signed64W[] = {'%','I','6','4','d',0};
135     static const WCHAR fmt_unsigned64W[] = {'%','I','6','4','u',0};
136     static const WCHAR fmt_strW[] = {'\"','%','s','\"',0};
137     LONGLONG val;
138     BSTR ret;
139     WCHAR number[22];
140     UINT len;
141
142     if (table->columns[column].type & CIM_FLAG_ARRAY)
143     {
144         FIXME("array to string conversion not handled\n");
145         return NULL;
146     }
147     if (get_value( table, row, column, &val ) != S_OK) return NULL;
148
149     switch (table->columns[column].type & COL_TYPE_MASK)
150     {
151     case CIM_DATETIME:
152     case CIM_STRING:
153         len = strlenW( (const WCHAR *)(INT_PTR)val ) + 2;
154         if (!(ret = SysAllocStringLen( NULL, len ))) return NULL;
155         sprintfW( ret, fmt_strW, (const WCHAR *)(INT_PTR)val );
156         return ret;
157
158     case CIM_SINT16:
159     case CIM_SINT32:
160         sprintfW( number, fmt_signedW, val );
161         return SysAllocString( number );
162
163     case CIM_UINT16:
164     case CIM_UINT32:
165         sprintfW( number, fmt_unsignedW, val );
166         return SysAllocString( number );
167
168     case CIM_SINT64:
169         wsprintfW( number, fmt_signed64W, val );
170         return SysAllocString( number );
171
172     case CIM_UINT64:
173         wsprintfW( number, fmt_unsigned64W, val );
174         return SysAllocString( number );
175
176     default:
177         FIXME("unhandled column type %u\n", table->columns[column].type & COL_TYPE_MASK);
178         break;
179     }
180     return NULL;
181 }
182
183 HRESULT create_view( const struct property *proplist, const WCHAR *class,
184                      const struct expr *cond, struct view **ret )
185 {
186     struct view *view = heap_alloc( sizeof(struct view) );
187
188     if (!view) return E_OUTOFMEMORY;
189     view->proplist = proplist;
190     view->table    = get_table( class );
191     view->cond     = cond;
192     view->result   = NULL;
193     view->count    = 0;
194     *ret = view;
195     return S_OK;
196 }
197
198 static void clear_table( struct table *table )
199 {
200     UINT i, j, type;
201
202     if (!table->fill || !table->data) return;
203
204     for (i = 0; i < table->num_rows; i++)
205     {
206         for (j = 0; j < table->num_cols; j++)
207         {
208             if (!(table->columns[j].type & COL_FLAG_DYNAMIC)) continue;
209
210             type = table->columns[j].type & COL_TYPE_MASK;
211             if (type == CIM_STRING || type == CIM_DATETIME || (type & CIM_FLAG_ARRAY))
212             {
213                 void *ptr;
214                 if (get_value( table, i, j, (LONGLONG *)&ptr ) == S_OK) heap_free( ptr );
215             }
216         }
217     }
218     heap_free( table->data );
219     table->data = NULL;
220 }
221
222 void destroy_view( struct view *view )
223 {
224     if (view->table) clear_table( view->table );
225     heap_free( view->result );
226     heap_free( view );
227 }
228
229 static BOOL eval_like( LONGLONG lval, LONGLONG rval )
230 {
231     const WCHAR *p = (const WCHAR *)(INT_PTR)lval, *q = (const WCHAR *)(INT_PTR)rval;
232
233     while (*p && *q)
234     {
235         if (*q == '%')
236         {
237             while (*q == '%') q++;
238             if (!*q) return TRUE;
239             while (*p && toupperW( p[1] ) != toupperW( q[1] )) p++;
240             if (!*p) return TRUE;
241         }
242         if (toupperW( *p++ ) != toupperW( *q++ )) return FALSE;
243     }
244     return TRUE;
245 }
246
247 static HRESULT eval_cond( const struct table *, UINT, const struct expr *, LONGLONG * );
248
249 static BOOL eval_binary( const struct table *table, UINT row, const struct complex_expr *expr,
250                          LONGLONG *val )
251 {
252     HRESULT lret, rret;
253     LONGLONG lval, rval;
254
255     lret = eval_cond( table, row, expr->left, &lval );
256     rret = eval_cond( table, row, expr->right, &rval );
257     if (lret != S_OK || rret != S_OK) return WBEM_E_INVALID_QUERY;
258
259     switch (expr->op)
260     {
261     case OP_EQ:
262         *val = (lval == rval);
263         break;
264     case OP_AND:
265         *val = (lval && rval);
266         break;
267     case OP_OR:
268         *val = (lval || rval);
269         break;
270     case OP_GT:
271         *val = (lval > rval);
272         break;
273     case OP_LT:
274         *val = (lval < rval);
275         break;
276     case OP_LE:
277         *val = (lval <= rval);
278         break;
279     case OP_GE:
280         *val = (lval >= rval);
281         break;
282     case OP_NE:
283         *val = (lval != rval);
284         break;
285     case OP_LIKE:
286         *val = eval_like( lval, rval );
287         break;
288     default:
289         ERR("unknown operator %u\n", expr->op);
290         return WBEM_E_INVALID_QUERY;
291     }
292     return S_OK;
293 }
294
295 static HRESULT eval_unary( const struct table *table, UINT row, const struct complex_expr *expr,
296                            LONGLONG *val )
297
298 {
299     HRESULT hr;
300     UINT column;
301     LONGLONG lval;
302
303     hr = get_column_index( table, expr->left->u.propval->name, &column );
304     if (hr != S_OK)
305         return hr;
306
307     hr = get_value( table, row, column, &lval );
308     if (hr != S_OK)
309         return hr;
310
311     switch (expr->op)
312     {
313     case OP_ISNULL:
314         *val = !lval;
315         break;
316     case OP_NOTNULL:
317         *val = lval;
318         break;
319     default:
320         ERR("unknown operator %u\n", expr->op);
321         return WBEM_E_INVALID_QUERY;
322     }
323     return S_OK;
324 }
325
326 static HRESULT eval_propval( const struct table *table, UINT row, const struct property *propval,
327                              LONGLONG *val )
328
329 {
330     HRESULT hr;
331     UINT column;
332
333     hr = get_column_index( table, propval->name, &column );
334     if (hr != S_OK)
335         return hr;
336
337     return get_value( table, row, column, val );
338 }
339
340 static HRESULT eval_cond( const struct table *table, UINT row, const struct expr *cond,
341                           LONGLONG *val )
342 {
343     if (!cond)
344     {
345         *val = 1;
346         return S_OK;
347     }
348     switch (cond->type)
349     {
350     case EXPR_COMPLEX:
351         return eval_binary( table, row, &cond->u.expr, val );
352     case EXPR_UNARY:
353         return eval_unary( table, row, &cond->u.expr, val );
354     case EXPR_PROPVAL:
355         return eval_propval( table, row, cond->u.propval, val );
356     case EXPR_SVAL:
357         *val = (LONGLONG)(INT_PTR)cond->u.sval;
358         return S_OK;
359     case EXPR_IVAL:
360     case EXPR_BVAL:
361         *val = cond->u.ival;
362         return S_OK;
363     default:
364         ERR("invalid expression type\n");
365         break;
366     }
367     return WBEM_E_INVALID_QUERY;
368 }
369
370 static HRESULT execute_view( struct view *view )
371 {
372     UINT i, j = 0, len;
373
374     if (!view->table || !view->table->num_rows) return S_OK;
375
376     len = min( view->table->num_rows, 16 );
377     if (!(view->result = heap_alloc( len * sizeof(UINT) ))) return E_OUTOFMEMORY;
378
379     for (i = 0; i < view->table->num_rows; i++)
380     {
381         HRESULT hr;
382         LONGLONG val = 0;
383
384         if (j >= len)
385         {
386             UINT *tmp;
387             len *= 2;
388             if (!(tmp = heap_realloc( view->result, len * sizeof(UINT) ))) return E_OUTOFMEMORY;
389             view->result = tmp;
390         }
391         if ((hr = eval_cond( view->table, i, view->cond, &val )) != S_OK) return hr;
392         if (val) view->result[j++] = i;
393     }
394     view->count = j;
395     return S_OK;
396 }
397
398 static struct query *create_query(void)
399 {
400     struct query *query;
401
402     if (!(query = heap_alloc( sizeof(*query) ))) return NULL;
403     list_init( &query->mem );
404     query->refs = 1;
405     return query;
406 }
407
408 static void free_query( struct query *query )
409 {
410     struct list *mem, *next;
411
412     destroy_view( query->view );
413     LIST_FOR_EACH_SAFE( mem, next, &query->mem )
414     {
415         heap_free( mem );
416     }
417     heap_free( query );
418 }
419
420 void addref_query( struct query *query )
421 {
422     InterlockedIncrement( &query->refs );
423 }
424
425 void release_query( struct query *query )
426 {
427     if (!InterlockedDecrement( &query->refs )) free_query( query );
428 }
429
430 HRESULT exec_query( const WCHAR *str, IEnumWbemClassObject **result )
431 {
432     HRESULT hr;
433     struct query *query;
434
435     *result = NULL;
436     if (!(query = create_query())) return E_OUTOFMEMORY;
437     hr = parse_query( str, &query->view, &query->mem );
438     if (hr != S_OK) goto done;
439     hr = execute_view( query->view );
440     if (hr != S_OK) goto done;
441     hr = EnumWbemClassObject_create( NULL, query, (void **)result );
442
443 done:
444     release_query( query );
445     return hr;
446 }
447
448 static BOOL is_selected_prop( const struct view *view, const WCHAR *name )
449 {
450     const struct property *prop = view->proplist;
451
452     if (!prop) return TRUE;
453     while (prop)
454     {
455         if (!strcmpiW( prop->name, name )) return TRUE;
456         prop = prop->next;
457     }
458     return FALSE;
459 }
460
461 static BOOL is_system_prop( const WCHAR *name )
462 {
463     return (name[0] == '_' && name[1] == '_');
464 }
465
466 static BSTR build_servername( const struct view *view )
467 {
468     WCHAR server[MAX_COMPUTERNAME_LENGTH + 1], *p;
469     DWORD len = sizeof(server)/sizeof(server[0]);
470
471     if (view->proplist) return NULL;
472
473     if (!(GetComputerNameW( server, &len ))) return NULL;
474     for (p = server; *p; p++) *p = toupperW( *p );
475     return SysAllocString( server );
476 }
477
478 static BSTR build_classname( const struct view *view )
479 {
480     return SysAllocString( view->table->name );
481 }
482
483 static BSTR build_namespace( const struct view *view )
484 {
485     static const WCHAR cimv2W[] = {'R','O','O','T','\\','C','I','M','V','2',0};
486
487     if (view->proplist) return NULL;
488     return SysAllocString( cimv2W );
489 }
490
491 static BSTR build_proplist( const struct view *view, UINT index, UINT count, UINT *len )
492 {
493     static const WCHAR fmtW[] = {'%','s','=','%','s',0};
494     UINT i, j, offset, row = view->result[index];
495     BSTR *values, ret = NULL;
496
497     if (!(values = heap_alloc( count * sizeof(BSTR) ))) return NULL;
498
499     *len = j = 0;
500     for (i = 0; i < view->table->num_cols; i++)
501     {
502         if (view->table->columns[i].type & COL_FLAG_KEY)
503         {
504             const WCHAR *name = view->table->columns[i].name;
505
506             values[j] = get_value_bstr( view->table, row, i );
507             *len += strlenW( fmtW ) + strlenW( name ) + strlenW( values[j] );
508             j++;
509         }
510     }
511     if ((ret = SysAllocStringLen( NULL, *len )))
512     {
513         offset = j = 0;
514         for (i = 0; i < view->table->num_cols; i++)
515         {
516             if (view->table->columns[i].type & COL_FLAG_KEY)
517             {
518                 const WCHAR *name = view->table->columns[i].name;
519
520                 offset += sprintfW( ret + offset, fmtW, name, values[j] );
521                 if (j < count - 1) ret[offset++] = ',';
522                 j++;
523             }
524         }
525     }
526     for (i = 0; i < count; i++) SysFreeString( values[i] );
527     heap_free( values );
528     return ret;
529 }
530
531 static UINT count_key_columns( const struct view *view )
532 {
533     UINT i, num_keys = 0;
534
535     for (i = 0; i < view->table->num_cols; i++)
536     {
537         if (view->table->columns[i].type & COL_FLAG_KEY) num_keys++;
538     }
539     return num_keys;
540 }
541
542 static BSTR build_relpath( const struct view *view, UINT index, const WCHAR *name )
543 {
544     static const WCHAR fmtW[] = {'%','s','.','%','s',0};
545     BSTR class, proplist, ret = NULL;
546     UINT num_keys, len;
547
548     if (view->proplist) return NULL;
549
550     if (!(class = build_classname( view ))) return NULL;
551     if (!(num_keys = count_key_columns( view ))) return class;
552     if (!(proplist = build_proplist( view, index, num_keys, &len ))) goto done;
553
554     len += strlenW( fmtW ) + SysStringLen( class );
555     if (!(ret = SysAllocStringLen( NULL, len ))) goto done;
556     sprintfW( ret, fmtW, class, proplist );
557
558 done:
559     SysFreeString( class );
560     SysFreeString( proplist );
561     return ret;
562 }
563
564 static BSTR build_path( const struct view *view, UINT index, const WCHAR *name )
565 {
566     static const WCHAR fmtW[] = {'\\','\\','%','s','\\','%','s',':','%','s',0};
567     BSTR server, namespace = NULL, relpath = NULL, ret = NULL;
568     UINT len;
569
570     if (view->proplist) return NULL;
571
572     if (!(server = build_servername( view ))) return NULL;
573     if (!(namespace = build_namespace( view ))) goto done;
574     if (!(relpath = build_relpath( view, index, name ))) goto done;
575
576     len = strlenW( fmtW ) + SysStringLen( server ) + SysStringLen( namespace ) + SysStringLen( relpath );
577     if (!(ret = SysAllocStringLen( NULL, len ))) goto done;
578     sprintfW( ret, fmtW, server, namespace, relpath );
579
580 done:
581     SysFreeString( server );
582     SysFreeString( namespace );
583     SysFreeString( relpath );
584     return ret;
585 }
586
587 static UINT count_selected_props( const struct view *view )
588 {
589     const struct property *prop = view->proplist;
590     UINT count;
591
592     if (!prop) return view->table->num_cols;
593
594     count = 1;
595     while ((prop = prop->next)) count++;
596     return count;
597 }
598
599 static HRESULT get_system_propval( const struct view *view, UINT index, const WCHAR *name,
600                                    VARIANT *ret, CIMTYPE *type, LONG *flavor )
601 {
602     static const WCHAR classW[] = {'_','_','C','L','A','S','S',0};
603     static const WCHAR genusW[] = {'_','_','G','E','N','U','S',0};
604     static const WCHAR pathW[] = {'_','_','P','A','T','H',0};
605     static const WCHAR namespaceW[] = {'_','_','N','A','M','E','S','P','A','C','E',0};
606     static const WCHAR propcountW[] = {'_','_','P','R','O','P','E','R','T','Y','_','C','O','U','N','T',0};
607     static const WCHAR relpathW[] = {'_','_','R','E','L','P','A','T','H',0};
608     static const WCHAR serverW[] = {'_','_','S','E','R','V','E','R',0};
609
610     if (flavor) *flavor = WBEM_FLAVOR_ORIGIN_SYSTEM;
611
612     if (!strcmpiW( name, classW ))
613     {
614         V_VT( ret ) = VT_BSTR;
615         V_BSTR( ret ) = build_classname( view );
616         if (type) *type = CIM_STRING;
617         return S_OK;
618     }
619     if (!strcmpiW( name, genusW ))
620     {
621         V_VT( ret ) = VT_I4;
622         V_I4( ret ) = WBEM_GENUS_INSTANCE; /* FIXME */
623         if (type) *type = CIM_SINT32;
624         return S_OK;
625     }
626     else if (!strcmpiW( name, namespaceW ))
627     {
628         V_VT( ret ) = VT_BSTR;
629         V_BSTR( ret ) = build_namespace( view );
630         if (type) *type = CIM_STRING;
631         return S_OK;
632     }
633     else if (!strcmpiW( name, pathW ))
634     {
635         V_VT( ret ) = VT_BSTR;
636         V_BSTR( ret ) = build_path( view, index, name );
637         if (type) *type = CIM_STRING;
638         return S_OK;
639     }
640     if (!strcmpiW( name, propcountW ))
641     {
642         V_VT( ret ) = VT_I4;
643         V_I4( ret ) = count_selected_props( view );
644         if (type) *type = CIM_SINT32;
645         return S_OK;
646     }
647     else if (!strcmpiW( name, relpathW ))
648     {
649         V_VT( ret ) = VT_BSTR;
650         V_BSTR( ret ) = build_relpath( view, index, name );
651         if (type) *type = CIM_STRING;
652         return S_OK;
653     }
654     else if (!strcmpiW( name, serverW ))
655     {
656         V_VT( ret ) = VT_BSTR;
657         V_BSTR( ret ) = build_servername( view );
658         if (type) *type = CIM_STRING;
659         return S_OK;
660     }
661     FIXME("system property %s not implemented\n", debugstr_w(name));
662     return WBEM_E_NOT_FOUND;
663 }
664
665 HRESULT get_propval( const struct view *view, UINT index, const WCHAR *name, VARIANT *ret,
666                      CIMTYPE *type, LONG *flavor )
667 {
668     HRESULT hr;
669     UINT column, row = view->result[index];
670     LONGLONG val;
671
672     if (is_system_prop( name )) return get_system_propval( view, index, name, ret, type, flavor );
673     if (!is_selected_prop( view, name )) return WBEM_E_NOT_FOUND;
674
675     hr = get_column_index( view->table, name, &column );
676     if (hr != S_OK) return WBEM_E_NOT_FOUND;
677
678     hr = get_value( view->table, row, column, &val );
679     if (hr != S_OK) return hr;
680
681     switch (view->table->columns[column].type & COL_TYPE_MASK)
682     {
683     case CIM_STRING:
684     case CIM_DATETIME:
685         V_VT( ret ) = VT_BSTR;
686         V_BSTR( ret ) = SysAllocString( (const WCHAR *)(INT_PTR)val );
687         break;
688     case CIM_SINT16:
689         V_VT( ret ) = VT_I2;
690         V_I2( ret ) = val;
691         break;
692     case CIM_UINT16:
693         V_VT( ret ) = VT_UI2;
694         V_UI2( ret ) = val;
695         break;
696     case CIM_SINT32:
697         V_VT( ret ) = VT_I4;
698         V_I4( ret ) = val;
699         break;
700     case CIM_UINT32:
701         V_VT( ret ) = VT_UI4;
702         V_UI4( ret ) = val;
703         break;
704     case CIM_SINT64:
705         V_VT( ret ) = VT_I8;
706         V_I8( ret ) = val;
707         break;
708     case CIM_UINT64:
709         V_VT( ret ) = VT_UI8;
710         V_UI8( ret ) = val;
711         break;
712     default:
713         ERR("unhandled column type %u\n", view->table->columns[column].type);
714         return WBEM_E_FAILED;
715     }
716     if (type) *type = view->table->columns[column].type & COL_TYPE_MASK;
717     if (flavor) *flavor = 0;
718     return S_OK;
719 }
720
721 HRESULT get_properties( const struct view *view, SAFEARRAY **props )
722 {
723     SAFEARRAY *sa;
724     BSTR str;
725     LONG i;
726
727     if (!(sa = SafeArrayCreateVector( VT_BSTR, 0, view->table->num_cols ))) return E_OUTOFMEMORY;
728
729     for (i = 0; i < view->table->num_cols; i++)
730     {
731         str = SysAllocString( view->table->columns[i].name );
732         if (!str || SafeArrayPutElement( sa, &i, str ) != S_OK)
733         {
734             SysFreeString( str );
735             SafeArrayDestroy( sa );
736             return E_OUTOFMEMORY;
737         }
738     }
739     *props = sa;
740     return S_OK;
741 }