|
1 /* |
|
2 * Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: |
|
15 * |
|
16 */ |
|
17 |
|
18 #include <e32base.h> |
|
19 |
|
20 #include <bautils.h> |
|
21 #include <obexconstants.h> |
|
22 #include "simpleObexClient.h" |
|
23 #include "simpleObexApp.h" |
|
24 #include "obexAppConstants.h" |
|
25 |
|
26 |
|
27 |
|
28 /** |
|
29 * Constructor, ownership of Console is passed. |
|
30 */ |
|
31 CObexClientHandler::CObexClientHandler(CActiveConsole* aParent) |
|
32 : CActive(CActive::EPriorityStandard), |
|
33 iParent(aParent) |
|
34 { |
|
35 iClient = NULL; |
|
36 iObject = NULL; |
|
37 iState = EIdle; |
|
38 } |
|
39 |
|
40 |
|
41 /** |
|
42 * NewL function. |
|
43 */ |
|
44 CObexClientHandler* CObexClientHandler::NewL(CActiveConsole* aParent, TTransport aTransport) |
|
45 { |
|
46 CObexClientHandler* self = new (ELeave) CObexClientHandler(aParent); |
|
47 |
|
48 CleanupStack::PushL (self); |
|
49 self->ConstructL(aTransport); |
|
50 CActiveScheduler::Add (self); |
|
51 CleanupStack::Pop (); |
|
52 return (self); |
|
53 } |
|
54 |
|
55 |
|
56 /** |
|
57 * 2nd phase constructor. Responsible for determining which |
|
58 * transport has been selected and to create a new OBEX Client. |
|
59 */ |
|
60 void CObexClientHandler::ConstructL(TTransport aTransport) |
|
61 { |
|
62 |
|
63 // Construct client if bluetooth selected |
|
64 if (aTransport == EBluetooth) |
|
65 { |
|
66 iDevAddr = devAddr; // take the Bluetooth address |
|
67 |
|
68 //start the SDP Query |
|
69 delete iSdpServiceFinder; |
|
70 iSdpServiceFinder=0; |
|
71 iSdpServiceFinder = CRFCOMMServiceFinder::NewL(ftpUuid, iDevAddr, *this); |
|
72 // Start search process |
|
73 iSdpServiceFinder->FindPortL(); |
|
74 iParent->Console()->Printf(_L("\nSearching for SDP service....\n")); |
|
75 } |
|
76 else if (aTransport == EIrda) |
|
77 { |
|
78 // Create transport info |
|
79 TObexIrProtocolInfo transportInfo; |
|
80 //assigning the unique name for the irda transport |
|
81 transportInfo.iTransport = KObexIrTTPProtocol; |
|
82 //assigning irda specific attributes |
|
83 transportInfo.iClassName = KIrdaClassName; |
|
84 transportInfo.iAttributeName = KIrdaTransportAttrName; |
|
85 |
|
86 iClient = CObexClient::NewL (transportInfo); //create the obex client |
|
87 iParent->iMode = E_Client; |
|
88 } |
|
89 else if (aTransport == EUsb) |
|
90 { |
|
91 // Create transport info |
|
92 TObexUsbProtocolInfo aInfo; |
|
93 aInfo.iTransport = KObexUsbProtocol; |
|
94 aInfo.iInterfaceStringDescriptor = KClientInterfaceDescriptor; |
|
95 |
|
96 iClient = CObexClient::NewL (aInfo);// Create the CObexClient |
|
97 } |
|
98 else if (aTransport == EWin32Usb) |
|
99 { |
|
100 // Ensure win32usb Obex transport plugin exists |
|
101 // Create transport info |
|
102 TObexUsbProtocolInfo aInfo; |
|
103 aInfo.iTransport = KObexWin32UsbProtocol; |
|
104 aInfo.iInterfaceStringDescriptor = KClientInterfaceDescriptor; |
|
105 |
|
106 iClient = CObexClient::NewL (aInfo); |
|
107 } |
|
108 else |
|
109 { |
|
110 User::Invariant(); |
|
111 } |
|
112 |
|
113 // Setup Obex objects |
|
114 iFileObject = CObexFileObject::NewL(); |
|
115 iObjectBuffer = CBufFlat::NewL(KBufExpandSize); |
|
116 iObject = CObexBufObject::NewL(iObjectBuffer); |
|
117 iObexName = KNullDesC; |
|
118 iGetType = KObjectType; |
|
119 |
|
120 // Create file session and create a new private path on drive C |
|
121 RFs fileSession; |
|
122 fileSession.Connect(); |
|
123 |
|
124 fileSession.CreatePrivatePath(EDriveC); |
|
125 fileSession.SetSessionToPrivate(EDriveC); |
|
126 fileSession.SessionPath(iSessionPath); |
|
127 |
|
128 // Copy the files from oby file ro the newly created path |
|
129 BaflUtils::CopyFile(fileSession, KFilePath1, KFilename1,0); |
|
130 BaflUtils::CopyFile(fileSession, KFilePath2, KFilename2,0); |
|
131 BaflUtils::CopyFile(fileSession, KFilePath3, KFilename3,0); |
|
132 |
|
133 fileSession.Close(); |
|
134 |
|
135 // Assign file names |
|
136 iFilename1 = KFilename1; |
|
137 iFilename2 = KFilename2; |
|
138 iFilename3 = KFilename3; |
|
139 |
|
140 |
|
141 } |
|
142 |
|
143 |
|
144 /** |
|
145 * Destructor. |
|
146 */ |
|
147 CObexClientHandler::~CObexClientHandler() |
|
148 { |
|
149 // do cleanup |
|
150 Cancel(); |
|
151 delete iObject; |
|
152 delete iFileObject; |
|
153 delete iClient; |
|
154 delete iObjectBuffer; |
|
155 delete iTargetHeaderObject; |
|
156 delete iSdpServiceFinder; |
|
157 } |
|
158 |
|
159 /** |
|
160 * This function is called when the SDP search finishes. |
|
161 * If search was successful then the OBEX port number is |
|
162 * taken an used in starting the OBEX client. |
|
163 * If unsuccessful, go back to the initial menu. |
|
164 */ |
|
165 void CObexClientHandler::SearchResult(TInt aError, TUint8 aPort) |
|
166 { |
|
167 // Service not supported? |
|
168 if (aError != KErrNone) |
|
169 { |
|
170 iParent->Console()->Printf(_L("\r\n Could not find SDP service in remote device : error %d \r\n"),aError); |
|
171 iParent->iMode = E_Inactive; |
|
172 iParent->Cancel(); |
|
173 } |
|
174 |
|
175 |
|
176 // Set protocol info for bluetooth |
|
177 |
|
178 TObexBluetoothProtocolInfo aInfo; |
|
179 aInfo.iAddr.SetBTAddr(iDevAddr); |
|
180 aInfo.iAddr.SetPort(aPort); |
|
181 aInfo.iTransport = KObexRfcommProtocol; |
|
182 //now create the obex client... |
|
183 TRAPD(error,iClient = CObexClient::NewL (aInfo)); |
|
184 if (error) |
|
185 { |
|
186 iParent->Console()->Printf(_L("\r\n Could not create client! : error %d \r\n"),error); |
|
187 iParent->iMode = E_Inactive; |
|
188 iParent->Cancel(); |
|
189 } |
|
190 |
|
191 iParent->Console()->Printf(_L("\nSDP search complete OK!\n")); |
|
192 iParent->iMode = E_Client; |
|
193 |
|
194 iParent->RequestCharacter(); |
|
195 |
|
196 } |
|
197 |
|
198 |
|
199 |
|
200 /** |
|
201 * This function performs the actual connection of client to server. |
|
202 */ |
|
203 |
|
204 void CObexClientHandler::Connect() |
|
205 { |
|
206 if(IsActive()) |
|
207 { |
|
208 iParent->Console()->Printf(KAlreadyActive); |
|
209 return; |
|
210 } |
|
211 |
|
212 TObexConnectInfo iLocalInfo = iClient->LocalInfo(); |
|
213 iLocalInfo.iWho = KNullDesC8; |
|
214 iLocalInfo.iWho = EPOCIDENT; |
|
215 iLocalInfo.iWho.Append(KLocalInfoAppend); |
|
216 // Connect to Obex client |
|
217 iClient->Connect(iStatus); |
|
218 SetActive(); |
|
219 iState = EConnecting; |
|
220 } |
|
221 |
|
222 |
|
223 /** |
|
224 * This fucntion performs the disconnection from OBEX client and server |
|
225 */ |
|
226 void CObexClientHandler::Disconnect() |
|
227 { |
|
228 if(IsActive()) |
|
229 { |
|
230 iParent->Console()->Printf(KAlreadyActive); |
|
231 return; |
|
232 } |
|
233 // Disconnect from Obex client |
|
234 iClient->Disconnect(iStatus); |
|
235 SetActive(); |
|
236 iState = EDisconnecting; |
|
237 } |
|
238 |
|
239 |
|
240 /** |
|
241 * This function is called when an OBEX get is requested by name. |
|
242 * It takes the current name of the OBEX object, replaces it with |
|
243 * a user defined name. it then performs a Get on this object name. |
|
244 */ |
|
245 void CObexClientHandler::GetByNameL() |
|
246 { |
|
247 if(IsActive()) |
|
248 { |
|
249 iParent->Console()->Printf(KAlreadyActive); |
|
250 return; |
|
251 } |
|
252 |
|
253 iObject->Reset (); |
|
254 // Call SetName passing current name |
|
255 // This will set a newly selected name from the user |
|
256 SetName(iObexName); |
|
257 // Set the name |
|
258 iObject->SetNameL (iObexName); |
|
259 // Call get with required object |
|
260 iClient->Get(*iObject, iStatus); |
|
261 SetActive(); |
|
262 iState = EGetting; |
|
263 } |
|
264 |
|
265 |
|
266 |
|
267 /** |
|
268 * This function is a wrapper for the CObexClient::Put function. |
|
269 * It is responsible for setting up a new Obex object from a specified file. |
|
270 * It then performs the Put operation. |
|
271 */ |
|
272 void CObexClientHandler::Put(TDes& aFilename) |
|
273 { |
|
274 if(IsActive()) |
|
275 { |
|
276 iParent->Console()->Printf(KAlreadyActive); |
|
277 return; |
|
278 } |
|
279 |
|
280 // Setup the object from file |
|
281 TInt err = SetUpObjectFromFile (aFilename); |
|
282 // Advise user if unsuccessful |
|
283 if( err != KErrNone) |
|
284 { |
|
285 iParent->Console()->Printf(_L("\r\n Couldnt set up object : error %d \r\n"),err); |
|
286 Disconnect(); |
|
287 return; |
|
288 } |
|
289 // Call the put |
|
290 iClient->Put(*iFileObject,iStatus); |
|
291 SetActive(); |
|
292 iState = EPutting; |
|
293 } |
|
294 |
|
295 |
|
296 /** |
|
297 * This function is responsible for initiating a |
|
298 * client/server connection with authentication. |
|
299 * The password is set in source. |
|
300 */ |
|
301 void CObexClientHandler::ConnectWithAuthenticationL() |
|
302 { |
|
303 if(IsActive()) |
|
304 { |
|
305 iParent->Console()->Printf(KAlreadyActive); |
|
306 return; |
|
307 } |
|
308 // Set generic password |
|
309 iChallengePassword = KAuthPassword; |
|
310 // Connect client to server sending password |
|
311 iClient->ConnectL(iChallengePassword, iStatus); |
|
312 // Active the active object |
|
313 SetActive(); |
|
314 iState = EConnecting; |
|
315 } |
|
316 |
|
317 /** |
|
318 * This function is called from within the GetByName function. |
|
319 * It takes in the current object name and changes it to a user |
|
320 * defined alternative. |
|
321 */ |
|
322 void CObexClientHandler::SetName(TDes& aName) |
|
323 { |
|
324 TBuf<64> oldName; |
|
325 oldName = aName; |
|
326 |
|
327 TKeyCode aCode; |
|
328 TBuf<1> aChar; |
|
329 iParent->Console()->Printf(_L("\nEnter a name: %S"),&aName); |
|
330 FOREVER |
|
331 { |
|
332 aCode = iParent->Console()->Getch(); |
|
333 aChar.SetLength(0); |
|
334 aChar.Append(aCode); |
|
335 |
|
336 iParent->Console()->Printf(_L("%S"),&aChar); |
|
337 |
|
338 // If <CR> finish editing string |
|
339 if (aCode == EStdKeyDelete) |
|
340 break; |
|
341 |
|
342 // if <BS> remove last character |
|
343 if ((aCode == EStdKeyHome)&&(aName.Length() != 0)) |
|
344 aName.SetLength((aName.Length()-1)); |
|
345 else |
|
346 aName.Append(aCode); |
|
347 } |
|
348 iParent->Console()->Printf(_L("\n")); |
|
349 |
|
350 |
|
351 } |
|
352 |
|
353 |
|
354 |
|
355 /** |
|
356 * This is the active object RunL function. |
|
357 * When the function is called the currently selected mode of operation |
|
358 * is examined and then dealt with accordingly. |
|
359 * It displays the active object iStatus current state to the user. |
|
360 */ |
|
361 void CObexClientHandler::RunL () |
|
362 { |
|
363 if (iStatus != KErrNone) |
|
364 {// Handle error |
|
365 } |
|
366 |
|
367 TBuf<80> filename; |
|
368 switch (iState) |
|
369 { |
|
370 // If we were connecting, and iStatus has completed then we want to change iState to EConnected |
|
371 case EConnecting: |
|
372 iParent->Console()->Printf(_L("\r\nConnect completed with error code: %d\r\n\r\n"),iStatus.Int()); |
|
373 iState = EConnected; |
|
374 break; |
|
375 // If we were performing a put and the iStatus has completed change iState to EConnected |
|
376 case EPutting: |
|
377 iState = EConnected; |
|
378 iParent->Console()->Printf(_L("\r\nPut completed with error code: %d\r\n\r\n"),iStatus.Int()); |
|
379 break; |
|
380 // If we were performing a get and the iStatus has completed change iState to EConnected |
|
381 // Also we want to write the received object to file |
|
382 case EGetting: |
|
383 iState = EConnected; |
|
384 iParent->Console()->Printf(_L("\r\nGet completed with error code: %d\r\n\r\n"),iStatus.Int()); |
|
385 DisplayObjectInformation(); |
|
386 filename = iSessionPath; |
|
387 filename.Append(iObject->Name()); |
|
388 // Write objects data to file, test if the file already exists as well |
|
389 if (iObject->WriteToFile(filename) == KErrAlreadyExists) |
|
390 { |
|
391 iParent->Console()->Printf(_L("\r\nWrite failed, File Already Exists\n")); |
|
392 } |
|
393 iObject->Reset (); |
|
394 break; |
|
395 // If we wanted to disconnect and iStatus has completed, then go back to original state |
|
396 case EDisconnecting: |
|
397 iParent->Console()->Printf(_L("\r\nDisconnect completed with error code: %d\r\n\r\n"),iStatus.Int()); |
|
398 iState = EIdle; |
|
399 |
|
400 default: |
|
401 iParent->Console()->Printf(_L("\r\nTest Code is in an incorrect state: %d\r\n\r\n"),iState); |
|
402 break; |
|
403 } |
|
404 |
|
405 } |
|
406 |
|
407 /** |
|
408 * This is the active object DoCancel function. |
|
409 */ |
|
410 void CObexClientHandler::DoCancel() |
|
411 { |
|
412 delete iClient; |
|
413 iClient = NULL; |
|
414 } |
|
415 |
|
416 |
|
417 /** |
|
418 * DisplayObjectInformation prints information of the received object. |
|
419 */ |
|
420 void CObexClientHandler::DisplayObjectInformation() |
|
421 { |
|
422 // Display Contents of CBufFlat data on current console |
|
423 iParent->Console()->Printf(_L("Size of received object = %d\n"),iObjectBuffer->Size()); |
|
424 TDateTime dt = iObject->Time().DateTime(); |
|
425 iParent->Console()->Printf(_L("\r\nTimestamp: %d/%d/%d, %d:%d:%d\r\n\r\n"), |
|
426 dt.Day(), dt.Month()+1, dt.Year(), dt.Hour(), dt.Minute(), dt.Second()); |
|
427 |
|
428 TBuf8<1024> tempBuffer; |
|
429 iObjectBuffer->Read(0, tempBuffer, tempBuffer.MaxSize() < iObjectBuffer->Size() ? tempBuffer.MaxSize() : iObjectBuffer->Size()); |
|
430 // Printf fails with Descriptor bigger than X hundred bytes so write byte at a time |
|
431 for(TInt count = 0; count < tempBuffer.Size(); count++) |
|
432 { |
|
433 iParent->Console()->Printf(_L("%C"),tempBuffer[count]); |
|
434 } |
|
435 } |
|
436 |
|
437 |
|
438 |
|
439 /** |
|
440 * This function is called when a Put operation has started. |
|
441 * It takes a filename and then attempts to setup an OBEX object |
|
442 * from that file. Returns the appropriate error code, if the |
|
443 * file didn't exist then this function creates a new file of |
|
444 * the same name. |
|
445 */ |
|
446 TInt CObexClientHandler::SetUpObjectFromFile(TDes& aFilename) |
|
447 { |
|
448 // Try to create CObexFileObject with filename |
|
449 TRAPD (err, iFileObject->InitFromFileL (aFilename)); |
|
450 if (err != KErrNone) |
|
451 { |
|
452 RFs fs; |
|
453 RFile f; |
|
454 err = fs.Connect(); |
|
455 if (err== KErrNone) |
|
456 { |
|
457 err = fs.CreatePrivatePath(EDriveC); |
|
458 } |
|
459 |
|
460 if (err== KErrNone || err == KErrAlreadyExists ) |
|
461 { |
|
462 err = fs.SetSessionToPrivate(EDriveC); |
|
463 } |
|
464 |
|
465 if (err == KErrNone) |
|
466 { |
|
467 // File didn't exist so create a file of same name in it place |
|
468 err = f.Create (fs, aFilename, EFileShareExclusive | EFileWrite); |
|
469 } |
|
470 |
|
471 if (err != KErrNone) |
|
472 { |
|
473 iParent->Console()->Printf(_L("\r\nError reading '%s'.\r\nI tried to create this file for you, but failed to do that too. Sorry.\r\n\r\n"), aFilename.PtrZ ()); |
|
474 } |
|
475 else |
|
476 { |
|
477 f.Write (_L8("Test file for sending from EPOC\r\n\r\nLooks like obex is sending OK!!\r\n")); |
|
478 f.Close (); |
|
479 iParent->Console()->Printf(_L("\r\nFile '%s' did not exist, so I've created one.\r\nPlease try again.\r\n\r\n"), aFilename.PtrZ ()); |
|
480 } |
|
481 fs.Close (); |
|
482 |
|
483 } |
|
484 |
|
485 return (err); |
|
486 |
|
487 } |
|
488 |
|
489 |
|
490 |
|
491 /** |
|
492 * NewL function for CRFCOMMServiceFinder |
|
493 */ |
|
494 |
|
495 CRFCOMMServiceFinder* CRFCOMMServiceFinder::NewL( const TUUID& aServiceClass, |
|
496 const TBTDevAddr& aDevAddr, |
|
497 MRFCOMMServiceSeeker& aSeeker) |
|
498 { |
|
499 CRFCOMMServiceFinder* self= new (ELeave) CRFCOMMServiceFinder(aSeeker); |
|
500 CleanupStack::PushL(self); |
|
501 self->ConstructL(aDevAddr, aServiceClass); |
|
502 CleanupStack::Pop(); |
|
503 return (self); |
|
504 } |
|
505 |
|
506 /** |
|
507 * Destructor. |
|
508 */ |
|
509 CRFCOMMServiceFinder::~CRFCOMMServiceFinder() |
|
510 { |
|
511 delete iPattern; |
|
512 delete iAgent; |
|
513 } |
|
514 |
|
515 /** |
|
516 * Constructor. |
|
517 */ |
|
518 CRFCOMMServiceFinder::CRFCOMMServiceFinder(MRFCOMMServiceSeeker& aSeeker) |
|
519 : iSeeker(aSeeker) |
|
520 { |
|
521 } |
|
522 |
|
523 /** |
|
524 * 2nd phase constructor. Sets the seach pattern for SDP with the UUID supplied. |
|
525 * Then Creates a new SDP agent with the device address supplied. |
|
526 */ |
|
527 void CRFCOMMServiceFinder::ConstructL(const TBTDevAddr& aDevAddr, const TUUID& aServiceClass) |
|
528 { |
|
529 iPattern=CSdpSearchPattern::NewL(); |
|
530 iPattern->AddL(aServiceClass); |
|
531 iAgent=CSdpAgent::NewL(*this, aDevAddr); |
|
532 // Filter out classes that arent FTP |
|
533 iAgent->SetRecordFilterL(*iPattern); |
|
534 } |
|
535 |
|
536 /** |
|
537 * This function is responsible for calling the SDP agent NextRecordRequestL |
|
538 * function, this gets the next service record that the device has registered |
|
539 * in the SDP database. |
|
540 */ |
|
541 void CRFCOMMServiceFinder::FindPortL() |
|
542 { |
|
543 iPort=KNotAPort; //0xFF will never be returned from a query, |
|
544 //because RFCOMM server channels only |
|
545 //go up to 30. |
|
546 |
|
547 iAgent->NextRecordRequestL(); |
|
548 } |
|
549 |
|
550 |
|
551 /** |
|
552 * This function is from the MSdpAgentNotify interface and is called when there |
|
553 * are no more SDP records registered in the database. It calls SearchResult implemented |
|
554 * by this class. |
|
555 */ |
|
556 void CRFCOMMServiceFinder::NextRecordRequestComplete(TInt aError, TSdpServRecordHandle aHandle, TInt /*aTotalRecordsCount*/) |
|
557 { |
|
558 if(aError) |
|
559 iSeeker.SearchResult(aError,KNoPort); |
|
560 else |
|
561 { |
|
562 //We have the record, kick off the attribute request |
|
563 TRAPD(err,iAgent->AttributeRequestL(aHandle,KSdpAttrIdProtocolDescriptorList )); |
|
564 if(err) |
|
565 iSeeker.SearchResult(err,KNoPort); |
|
566 } |
|
567 } |
|
568 |
|
569 |
|
570 |
|
571 /** |
|
572 * This function comes from the MSdpAgentNotifer interface, It is called when |
|
573 * the attribute request process has finished. |
|
574 */ |
|
575 void CRFCOMMServiceFinder::AttributeRequestComplete(TSdpServRecordHandle /*aHandle*/, TInt aError) |
|
576 { |
|
577 if(aError!=KErrNone || iPort==KNotAPort) |
|
578 iSeeker.SearchResult(aError?aError:KErrNotFound,KNoPort); |
|
579 else |
|
580 iSeeker.SearchResult(KErrNone, iPort); |
|
581 } |
|
582 |
|
583 |
|
584 |
|
585 |