Initial commit - from Precise source
[freerdp-ubuntu-pcb-backport.git] / channels / rdpdr / smartcard / scard_main.c
1 /*
2    FreeRDP: A Remote Desktop Protocol client.
3    Redirected Smart Card Device Service
4
5    Copyright 2011 O.S. Systems Software Ltda.
6    Copyright 2011 Eduardo Fiss Beloni <beloni@ossystems.com.br>
7    Copyright 2011 Anthony Tong <atong@trustedcs.com>
8
9    Licensed under the Apache License, Version 2.0 (the "License");
10    you may not use this file except in compliance with the License.
11    You may obtain a copy of the License at
12
13        http://www.apache.org/licenses/LICENSE-2.0
14
15    Unless required by applicable law or agreed to in writing, software
16    distributed under the License is distributed on an "AS IS" BASIS,
17    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18    See the License for the specific language governing permissions and
19    limitations under the License.
20 */
21
22 #include "config.h"
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include <freerdp/utils/list.h>
29 #include <freerdp/utils/thread.h>
30 #include <freerdp/utils/svc_plugin.h>
31
32 #include "rdpdr_types.h"
33 #include "rdpdr_constants.h"
34
35 #include "scard_main.h"
36
37
38 static void
39 scard_free(DEVICE* dev)
40 {
41         SCARD_DEVICE* scard = (SCARD_DEVICE*)dev;
42         IRP* irp;
43
44         freerdp_thread_stop(scard->thread);
45         freerdp_thread_free(scard->thread);
46         
47         while ((irp = (IRP*)list_dequeue(scard->irp_list)) != NULL)
48                 irp->Discard(irp);
49         list_free(scard->irp_list);
50
51         xfree(dev);
52         return;
53 }
54
55
56 static void
57 scard_process_irp(SCARD_DEVICE* scard, IRP* irp)
58 {
59         switch (irp->MajorFunction)
60         {
61                 case IRP_MJ_DEVICE_CONTROL:
62                         scard_device_control(scard, irp);
63                         break;
64
65                 default:
66                         printf("MajorFunction 0x%X unexpected for smartcards.", irp->MajorFunction);
67                         DEBUG_WARN("Smartcard MajorFunction 0x%X not supported.", irp->MajorFunction);
68                         irp->IoStatus = STATUS_NOT_SUPPORTED;
69                         irp->Complete(irp);
70                         break;
71         }
72 }
73
74
75 static void
76 scard_process_irp_list(SCARD_DEVICE* scard)
77 {
78         IRP *irp;
79
80         while (!freerdp_thread_is_stopped(scard->thread))
81         {
82                 freerdp_thread_lock(scard->thread);
83                 irp = (IRP *) list_dequeue(scard->irp_list);
84                 freerdp_thread_unlock(scard->thread);
85
86                 if (irp == NULL)
87                         break;
88
89                 scard_process_irp(scard, irp);
90         }
91 }
92
93
94 struct scard_irp_thread_args {
95         SCARD_DEVICE* scard;
96         IRP* irp;
97         freerdp_thread* thread;
98 };
99  
100
101 static void
102 scard_process_irp_thread_func(struct scard_irp_thread_args* args)
103 {
104         scard_process_irp(args->scard, args->irp);
105
106         freerdp_thread_free(args->thread);
107         xfree(args);
108 }
109
110
111 static void *
112 scard_thread_func(void* arg)
113 {
114         SCARD_DEVICE* scard = (SCARD_DEVICE*) arg;
115
116         while (1)
117         {
118                 freerdp_thread_wait(scard->thread);
119
120                 if (freerdp_thread_is_stopped(scard->thread))
121                         break;
122
123                 freerdp_thread_reset(scard->thread);
124                 scard_process_irp_list(scard);
125         }
126
127         freerdp_thread_quit(scard->thread);
128
129         return NULL;
130 }
131
132
133 static void
134 scard_irp_request(DEVICE* device, IRP* irp)
135 {
136         SCARD_DEVICE* scard = (SCARD_DEVICE*)device;
137
138         if (irp->MajorFunction == IRP_MJ_DEVICE_CONTROL &&
139                         scard_async_op(irp))
140         {
141                 /*
142                  * certain potentially long running operations
143                  * get their own thread
144                  * TODO: revise this mechanism.. maybe worker pool
145                  */
146                 struct scard_irp_thread_args *args = xmalloc(sizeof(struct scard_irp_thread_args));
147
148
149                 args->thread = freerdp_thread_new();
150                 args->scard = scard;
151                 args->irp = irp;
152                 freerdp_thread_start(args->thread, scard_process_irp_thread_func, args);
153
154                 return;
155         }
156
157         freerdp_thread_lock(scard->thread);
158         list_enqueue(scard->irp_list, irp);
159         freerdp_thread_unlock(scard->thread);
160
161         freerdp_thread_signal(scard->thread);
162 }
163
164
165 int
166 DeviceServiceEntry(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints)
167 {
168         SCARD_DEVICE* scard;
169         char* name;
170         char* path;
171         int i, length;
172
173         name = (char *)pEntryPoints->plugin_data->data[1];
174         path = (char *)pEntryPoints->plugin_data->data[2];
175
176         if (name)
177         {
178                 /* TODO: check if server supports sc redirect (version 5.1) */
179
180                 scard = xnew(SCARD_DEVICE);
181
182                 scard->device.type = RDPDR_DTYP_SMARTCARD;
183                 scard->device.name = "SCARD";
184                 scard->device.IRPRequest = scard_irp_request;
185                 scard->device.Free = scard_free;
186
187                 length = strlen(scard->device.name);
188                 scard->device.data = stream_new(length + 1);
189
190                 for (i = 0; i <= length; i++)
191                         stream_write_uint8(scard->device.data, name[i] < 0 ? '_' : name[i]);
192
193                 scard->path = path;
194
195                 scard->irp_list = list_new();
196                 scard->thread = freerdp_thread_new();
197
198                 pEntryPoints->RegisterDevice(pEntryPoints->devman, (DEVICE *)scard);
199
200                 freerdp_thread_start(scard->thread, scard_thread_func, scard);
201         }
202
203         return 0;
204 }