Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
THostAuth.cxx
Go to the documentation of this file.
1 // @(#)root/auth:$Id$
2 // Author: G. Ganis 19/03/2003
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers. *
6  * All rights reserved. *
7  * *
8  * For the licensing terms see $ROOTSYS/LICENSE. *
9  * For the list of contributors see $ROOTSYS/README/CREDITS. *
10  *************************************************************************/
11 
12 //////////////////////////////////////////////////////////////////////////
13 // //
14 // THostAuth //
15 // //
16 // Contains details about host-specific authentication methods and the //
17 // result of their application. //
18 // Used by TAuthenticate. //
19 // //
20 //////////////////////////////////////////////////////////////////////////
21 
22 #include "RConfigure.h"
23 #include "TSystem.h"
24 #include "THostAuth.h"
25 #include "TRootSecContext.h"
26 #include "TAuthenticate.h"
27 #include "TSocket.h"
28 #include "TUrl.h"
29 #include <stdlib.h>
30 
31 
32 ClassImp(THostAuth);
33 
34 ////////////////////////////////////////////////////////////////////////////////
35 /// Default constructor.
36 
37  THostAuth::THostAuth() : TObject()
38 {
39  Create(0, 0);
40 }
41 
42 ////////////////////////////////////////////////////////////////////////////////
43 /// Create hostauth object.
44 /// 'host' may contain also the server for whicb these directives
45 /// are valid in the form 'host:server' or 'server://host'
46 /// with server either "sock[d]", "root[d]", "proof[d]" or
47 /// 0, 1, 2, respectively.
48 
49 THostAuth::THostAuth(const char *host, const char *user, Int_t nmeth,
50  Int_t *authmeth, char **details) : TObject()
51 {
52  Create(host, user, nmeth, authmeth, details);
53 }
54 
55 ////////////////////////////////////////////////////////////////////////////////
56 /// Create hostauth object.
57 /// 'host' may contain also the server for whicb these directives
58 /// are valid in the form 'host:server' or 'server://host'
59 /// with server either "sock[d]", "root[d]", "proof[d]" or
60 /// 0, 1, 2, respectively.
61 
62 THostAuth::THostAuth(const char *host, Int_t server, const char *user,
63  Int_t nmeth, Int_t *authmeth, char **details) : TObject()
64 {
65  Create(host, user, nmeth, authmeth, details);
66 
67  fServer = server;
68 }
69 
70 ////////////////////////////////////////////////////////////////////////////////
71 /// Create hostauth object with one method only.
72 /// 'host' may contain also the server for whicb these directives
73 /// are valid in the form 'host:server' or 'server://host'
74 
75 THostAuth::THostAuth(const char *host, const char *user, Int_t authmeth,
76  const char *details) : TObject()
77 {
78  Create(host, user, 1, &authmeth, (char **)&details);
79 }
80 
81 ////////////////////////////////////////////////////////////////////////////////
82 /// Create hostauth object with one method only.
83 /// 'host' may contain also the server for whicb these directives
84 /// are valid in the form 'host:server' or 'server://host'
85 
86 THostAuth::THostAuth(const char *host, Int_t server, const char *user,
87  Int_t authmeth, const char *details) : TObject()
88 {
89  Create(host, user, 1, &authmeth, (char **)&details);
90  fServer = server;
91 }
92 
93 ////////////////////////////////////////////////////////////////////////////////
94 /// Create hostauth object.
95 /// 'host' may contain also the server for whicb these directives
96 /// are valid in the form 'host:server' or 'server://host'
97 /// with server either "sock[d]", "root[d]", "proof[d]" or
98 /// 0, 1, 2, respectively.
99 
100 void THostAuth::Create(const char *host, const char *user, Int_t nmeth,
101  Int_t *authmeth, char **details)
102 {
103  int i;
104 
105  // Host
106  fHost = host;
107 
108  fServer = -1;
109  // Extract server, if given
110  TString srv("");
111  if (fHost.Contains(":")) {
112  // .rootauthrc form: host:server
113  srv = fHost;
114  fHost.Remove(fHost.Index(":"));
115  srv.Remove(0,srv.Index(":")+1);
116  } else if (fHost.Contains("://")) {
117  // Url form: server://host
118  srv = TUrl(fHost).GetProtocol();
119  fHost.Remove(0,fHost.Index("://")+3);
120  }
121  if (srv.Length()) {
122  if (srv == "0" || srv.BeginsWith("sock"))
123  fServer = TSocket::kSOCKD;
124  else if (srv == "1" || srv.BeginsWith("root"))
125  fServer = TSocket::kROOTD;
126  else if (srv == "2" || srv.BeginsWith("proof"))
127  fServer = TSocket::kPROOFD;
128  }
129 
130  // Check and save the host FQDN ...
131  if (fHost != "default" && !fHost.Contains("*")) {
132  TInetAddress addr = gSystem->GetHostByName(fHost);
133  if (addr.IsValid())
134  fHost = addr.GetHostName();
135  }
136 
137  // User
138  fUser = user;
139  if (fUser == "")
140  fUser = gSystem->Getenv("USER");
141  if (fUser == "") {
142  UserGroup_t *u = gSystem->GetUserInfo();
143  if (u)
144  fUser = u->fUser;
145  delete u;
146  }
147 
148  // Methods indexes
149  fNumMethods = nmeth;
150  if (fNumMethods > 0) {
151  if (!authmeth)
152  fNumMethods = 0;
153  for (i = 0; i < kMAXSEC; i++) {
154  if (i < fNumMethods) {
155  fMethods[i] = authmeth[i];
156  fSuccess[i] = 0;
157  fFailure[i] = 0;
158  } else {
159  fMethods[i] = -1;
160  fSuccess[i] = -1;
161  fFailure[i] = -1;
162  }
163  }
164  }
165 
166  // Method details
167  if (fNumMethods > 0) {
168  for (i = 0; i < fNumMethods; i++) {
169  if (details && details[i] && strlen(details[i]) > 0) {
170  fDetails[i] = details[i];
171  } else {
172  // Use default instead
173  char *tmp = TAuthenticate::GetDefaultDetails(fMethods[i],0,fUser);
174  fDetails[i] = (const char *)tmp;
175  delete[] tmp;
176  }
177  }
178  }
179 
180  // List of TSecContext
181  fSecContexts = new TList;
182 
183  // Active when created
184  fActive = kTRUE;
185 }
186 
187 
188 ////////////////////////////////////////////////////////////////////////////////
189 /// Create hostauth object from directives given as a compact string
190 /// See THostAuth::AsString().
191 /// Used in proof context only; fServer not set; to be set by hand
192 /// with SetServer() method if really needed
193 
194 THostAuth::THostAuth(const char *asstring) : TObject()
195 {
196  fServer = -1;
197 
198  TString strtmp(asstring);
199  char *tmp = strdup(asstring);
200 
201  fHost = TString((const char *)strtok(tmp," "));
202  strtmp.ReplaceAll(fHost,"");
203  fHost.Remove(0,fHost.Index(":")+1);
204 
205  fUser = TString((const char *)strtok(0," "));
206  strtmp.ReplaceAll(fUser,"");
207  fUser.Remove(0,fUser.Index(":")+1);
208 
209  TString fNmet;
210  fNmet = TString((const char *)strtok(0," "));
211  strtmp.ReplaceAll(fNmet,"");
212  fNmet.Remove(0,fNmet.Index(":")+1);
213 
214  free(tmp);
215 
216  fNumMethods = atoi(fNmet.Data());
217  Int_t i = 0;
218  for (; i < fNumMethods; i++) {
219  TString det = strtmp;
220  det.Remove(0,det.Index("'")+1);
221  det.Resize(det.Index("'"));
222  // Remove leading spaces, if
223  char cmet[20];
224  sscanf(det.Data(),"%10s",cmet);
225  Int_t met = atoi(cmet);
226  if (met > -1 && met < kMAXSEC) {
227  det.ReplaceAll(cmet,"");
228  while (det.First(' ') == 0)
229  det.Remove(0,1);
230  while (det.Last(' ') == (det.Length() - 1))
231  det.Resize(det.Length() - 1);
232  fMethods[i] = met;
233  fSuccess[i] = 0;
234  fFailure[i] = 0;
235  fDetails[i] = det;
236  }
237  strtmp.Remove(0,strtmp.Index("'",strtmp.Index("'")+1)+1);
238  }
239  for (i = fNumMethods; i < kMAXSEC ; i++) {
240  fMethods[i] = -1;
241  fSuccess[i] = -1;
242  fFailure[i] = -1;
243  }
244 
245  // List of TSecContext
246  fSecContexts = new TList;
247 
248  // Active when created
249  fActive = kTRUE;
250 }
251 
252 
253 ////////////////////////////////////////////////////////////////////////////////
254 /// Copy ctor ...
255 
256 THostAuth::THostAuth(THostAuth &ha) : TObject()
257 {
258  fHost = ha.fHost;
259  fServer = ha.fServer;
260  fUser = ha.fUser;
261  fNumMethods = ha.fNumMethods;
262  Int_t i = 0;
263  for (; i < kMAXSEC; i++) {
264  fMethods[i] = ha.fMethods[i];
265  fSuccess[i] = ha.fSuccess[i];
266  fFailure[i] = ha.fFailure[i];
267  fDetails[i] = ha.fDetails[i];
268  }
269  fSecContexts = ha.Established();
270  fActive = ha.fActive;
271 }
272 
273 ////////////////////////////////////////////////////////////////////////////////
274 /// Add method to the list. If already there, change its
275 /// details to 'details'
276 
277 void THostAuth::AddMethod(Int_t meth, const char *details)
278 {
279  // Check 'meth'
280  if (meth < 0 || meth >= kMAXSEC) return;
281 
282  // If already there, set details and return
283  if (HasMethod(meth)) {
284  SetDetails(meth,details);
285  return;
286  }
287 
288  // This is a new method
289  fMethods[fNumMethods] = meth;
290  fSuccess[fNumMethods] = 0;
291  fFailure[fNumMethods] = 0;
292  if (details && strlen(details) > 0) {
293  fDetails[fNumMethods] = details;
294  } else {
295  // Use default instead
296  char *tmp = TAuthenticate::GetDefaultDetails(meth,0,fUser);
297  fDetails[fNumMethods] = (const char *)tmp;
298  delete[] tmp;
299  }
300 
301  // Increment total number
302  fNumMethods++;
303 
304  if (gDebug > 3) Print();
305 }
306 
307 ////////////////////////////////////////////////////////////////////////////////
308 /// Remove method 'meth' from the list, if there ...
309 
310 void THostAuth::RemoveMethod(Int_t meth)
311 {
312  // If we don't have it, nothing to do
313  Int_t pos = -1;
314  if (!HasMethod(meth,&pos)) return;
315 
316  // Now rescale info
317  Int_t i = 0, k = 0;
318  for (; i < fNumMethods; i++) {
319  if (i != pos) {
320  fMethods[k] = fMethods[i];
321  fSuccess[k] = fSuccess[i];
322  fFailure[k] = fFailure[i];
323  fDetails[k] = fDetails[i];
324  k++;
325  }
326  }
327 
328  // Decrement total number
329  fNumMethods--;
330 
331  // Free last position
332  fMethods[fNumMethods] = -1;
333  fSuccess[fNumMethods] = -1;
334  fFailure[fNumMethods] = -1;
335  fDetails[fNumMethods].Resize(0);
336 
337  if (gDebug > 3) Print();
338 }
339 
340 ////////////////////////////////////////////////////////////////////////////////
341 /// Remove all methods, leaving Active status and
342 /// list of associted TSceContexts unchanged
343 
344 void THostAuth::Reset()
345 {
346  // Free all filled positions
347  Int_t i = 0;
348  for (; i < fNumMethods; i++) {
349  fMethods[i] = -1;
350  fSuccess[i] = -1;
351  fFailure[i] = -1;
352  fDetails[i].Resize(0);
353  }
354 
355  // Set total number to 0
356  fNumMethods = 0;
357 }
358 
359 ////////////////////////////////////////////////////////////////////////////////
360 /// The dtor.
361 
362 THostAuth::~THostAuth()
363 {
364  delete fSecContexts;
365 }
366 
367 ////////////////////////////////////////////////////////////////////////////////
368 /// Return authentication details for specified level
369 /// or "" if the specified level does not exist for this host.
370 
371 const char *THostAuth::GetDetails(Int_t level)
372 {
373  Int_t i = -1;
374  if (HasMethod(level,&i)) {
375  if (gDebug > 3)
376  Info("GetDetails"," %d: returning fDetails[%d]: %s",
377  level,i,fDetails[i].Data());
378  return fDetails[i];
379  }
380  static const char *empty = " ";
381  return empty;
382 }
383 
384 ////////////////////////////////////////////////////////////////////////////////
385 /// Return kTRUE if method 'level' is in the list
386 
387 Bool_t THostAuth::HasMethod(Int_t level, Int_t *pos)
388 {
389  int i;
390  for (i = 0; i < fNumMethods; i++) {
391  if (fMethods[i] == level) {
392  if (pos) *pos = i;
393  return kTRUE;
394  }
395  }
396  if (pos) *pos = -1;
397  return kFALSE;
398 }
399 
400 ////////////////////////////////////////////////////////////////////////////////
401 /// Set authentication details for specified level.
402 
403 void THostAuth::SetDetails(Int_t level, const char *details)
404 {
405  Int_t i = -1;
406  if (HasMethod(level,&i)) {
407  if (details && strlen(details) > 0) {
408  fDetails[i] = details;
409  } else {
410  // Use default instead
411  char *tmp = TAuthenticate::GetDefaultDetails(level,0,fUser);
412  fDetails[i] = (const char *)tmp;
413  delete[] tmp;
414  }
415  } else {
416  // Add new method ...
417  AddMethod(level, details);
418  }
419 }
420 
421 ////////////////////////////////////////////////////////////////////////////////
422 /// Print object content.
423 
424 void THostAuth::Print(Option_t *proc) const
425 {
426  char srvnam[5][8] = { "any", "sockd", "rootd", "proofd", "???" };
427 
428  Int_t isrv = (fServer >= -1 && fServer <= TSocket::kPROOFD) ?
429  fServer+1 : TSocket::kPROOFD+2;
430 
431  Info("Print",
432  "%s +------------------------------------------------------------------+",proc);
433  Info("Print","%s + Host:%s - srv:%s - User:%s - # of available methods:%d",
434  proc, fHost.Data(), srvnam[isrv], fUser.Data(), fNumMethods);
435  int i = 0;
436  for (i = 0; i < fNumMethods; i++){
437  Info("Print","%s + Method: %d (%s) Ok:%d Ko:%d Dets:%s", proc,
438  fMethods[i],TAuthenticate::GetAuthMethod(fMethods[i]),
439  fSuccess[i], fFailure[i], fDetails[i].Data());
440  }
441  Info("Print",
442  "%s +------------------------------------------------------------------+",proc);
443 }
444 
445 ////////////////////////////////////////////////////////////////////////////////
446 /// Print info about established authentication vis-a-vis of this Host.
447 
448 void THostAuth::PrintEstablished() const
449 {
450  Info("PrintEstablished",
451  "+------------------------------------------------------------------------------+");
452  Info("PrintEstablished","+ Host:%s - Number of active sec contexts: %d",
453  fHost.Data(), fSecContexts->GetSize());
454 
455  // Check list
456  if (fSecContexts->GetSize()>0) {
457  TIter next(fSecContexts);
458  TSecContext *ctx = 0;
459  Int_t k = 1;
460  while ((ctx = (TSecContext *) next())) {
461  TString opt;
462  opt += k++;
463  ctx->Print(opt);
464  }
465  }
466  Info("PrintEstablished",
467  "+------------------------------------------------------------------------------+");
468 }
469 
470 ////////////////////////////////////////////////////////////////////////////////
471 /// Reorder nmet methods according fmet[nmet]
472 
473 void THostAuth::ReOrder(Int_t nmet, Int_t *fmet)
474 {
475  // Temporary arrays
476  Int_t tMethods[kMAXSEC] = {0};
477  Int_t tSuccess[kMAXSEC] = {0};
478  Int_t tFailure[kMAXSEC] = {0};
479  TString tDetails[kMAXSEC];
480  Int_t flag[kMAXSEC] = {0};
481 
482  // Copy info in the new order
483  Int_t j = 0;
484  for ( ; j < nmet; j++) {
485  Int_t i = -1;
486  if (HasMethod(fmet[j],&i)) {
487  tMethods[j] = fMethods[i];
488  tSuccess[j] = fSuccess[i];
489  tFailure[j] = fFailure[i];
490  tDetails[j] = fDetails[i];
491  flag[i]++;
492  } else if (fmet[j] >= 0 && fmet[j] < kMAXSEC) {
493  tMethods[j] = fmet[j];
494  tSuccess[j] = 0;
495  tFailure[j] = 0;
496  char *tmp = TAuthenticate::GetDefaultDetails(fmet[j],0,fUser);
497  tDetails[j] = (const char *)tmp;
498  delete[] tmp;
499  } else {
500  Warning("ReOrder","Method id out of range (%d) - skipping",fmet[j]);
501  }
502  }
503 
504  // Add existing methods not listed ... if any
505  Int_t k = nmet, i = 0;
506  for(; i < fNumMethods; i++){
507  if (flag[i] == 0) {
508  tMethods[k] = fMethods[i];
509  tSuccess[k] = fSuccess[i];
510  tFailure[k] = fFailure[i];
511  tDetails[k] = fDetails[i];
512  k++;
513  flag[i] = 1;
514  }
515  }
516 
517  // Restore from temporary
518  fNumMethods = k;
519  for (i = 0; i < fNumMethods; i++) {
520  fMethods[i] = tMethods[i];
521  fSuccess[i] = tSuccess[i];
522  fFailure[i] = tFailure[i];
523  fDetails[i] = tDetails[i];
524  }
525 
526  if (gDebug > 3) Print();
527 }
528 
529 ////////////////////////////////////////////////////////////////////////////////
530 /// Update info with the one in ha
531 /// Remaining methods, if any, get lower priority
532 
533 void THostAuth::Update(THostAuth *ha)
534 {
535  // Temporary arrays
536  Int_t tNumMethods = fNumMethods;
537  Int_t tMethods[kMAXSEC];
538  Int_t tSuccess[kMAXSEC];
539  Int_t tFailure[kMAXSEC];
540  TString tDetails[kMAXSEC];
541 
542  // Save existing info in temporary arrays
543  Int_t i = 0;
544  for ( ; i < fNumMethods; i++) {
545  tMethods[i] = fMethods[i];
546  tSuccess[i] = fSuccess[i];
547  tFailure[i] = fFailure[i];
548  tDetails[i] = fDetails[i];
549  }
550 
551  // Reset
552  Reset();
553 
554  // Get ha content in
555  for(i = 0; i < ha->NumMethods(); i++){
556  fMethods[i] = ha->GetMethod(i);
557  fSuccess[i] = ha->GetSuccess(i);
558  fFailure[i] = ha->GetFailure(i);
559  fDetails[i] = ha->GetDetailsByIdx(i);
560  }
561 
562  // Set new tmp size
563  fNumMethods = ha->NumMethods();
564 
565  // Add remaining methods with low priority
566  if (fNumMethods < kMAXSEC) {
567  for (i = 0; i < tNumMethods; i++) {
568  if (!HasMethod(tMethods[i]) && fNumMethods < kMAXSEC) {
569  fMethods[fNumMethods] = tMethods[i];
570  fSuccess[fNumMethods] = tSuccess[i];
571  fFailure[fNumMethods] = tFailure[i];
572  fDetails[fNumMethods] = tDetails[i];
573  fNumMethods++;
574  }
575  }
576  }
577  if (gDebug > 3) Print();
578 }
579 
580 ////////////////////////////////////////////////////////////////////////////////
581 /// Set 'method' to be the first used (if in the list ...).
582 
583 void THostAuth::SetFirst(Int_t method)
584 {
585  Int_t i = -1;
586  if (HasMethod(method,&i)) {
587 
588  Int_t tMe = fMethods[i];
589  Int_t tSu = fSuccess[i];
590  Int_t tFa = fFailure[i];
591  TString tDe = fDetails[i];
592 
593  // Rescale methods
594  Int_t j = i;
595  for (; j > 0; j--) {
596  fMethods[j] = fMethods[j-1];
597  fSuccess[j] = fSuccess[j-1];
598  fFailure[j] = fFailure[j-1];
599  fDetails[j] = fDetails[j-1];
600  }
601 
602  // The saved method first
603  fMethods[0] = tMe;
604  fSuccess[0] = tSu;
605  fFailure[0] = tFa;
606  fDetails[0] = tDe;
607  }
608 
609  if (gDebug > 3) Print();
610 }
611 
612 ////////////////////////////////////////////////////////////////////////////////
613 /// Set 'method' to be the last used (if in the list ...).
614 
615 void THostAuth::SetLast(Int_t method)
616 {
617  Int_t i = -1;
618  if (HasMethod(method,&i)) {
619 
620  Int_t tMe = fMethods[i];
621  Int_t tSu = fSuccess[i];
622  Int_t tFa = fFailure[i];
623  TString tDe = fDetails[i];
624 
625  // Rescale methods
626  Int_t j = i;
627  for (; j < (fNumMethods - 1); j++) {
628  fMethods[j] = fMethods[j+1];
629  fSuccess[j] = fSuccess[j+1];
630  fFailure[j] = fFailure[j+1];
631  fDetails[j] = fDetails[j+1];
632  }
633 
634  // The saved method first
635  Int_t lp = fNumMethods - 1;
636  fMethods[lp] = tMe;
637  fSuccess[lp] = tSu;
638  fFailure[lp] = tFa;
639  fDetails[lp] = tDe;
640  }
641 
642  if (gDebug > 3) Print();
643 }
644 
645 ////////////////////////////////////////////////////////////////////////////////
646 /// Add new method in first position
647 /// If already in the list, set as first method 'level' with
648 /// authentication 'details'.
649 /// Faster then AddMethod(method,details)+SetFirst(method).
650 
651 void THostAuth::AddFirst(Int_t level, const char *details)
652 {
653  Int_t i = -1;
654  if (HasMethod(level,&i)) {
655  if (i > 0) {
656  SetDetails(level, details);
657  SetFirst(level);
658  }
659  if (gDebug > 3) Print();
660  return;
661  }
662 
663  // Rescale methods
664  for (i = fNumMethods; i > 0; i--) {
665  fMethods[i] = fMethods[i-1];
666  fSuccess[i] = fSuccess[i-1];
667  fFailure[i] = fFailure[i-1];
668  fDetails[i] = fDetails[i-1];
669  }
670 
671  // This method first
672  fMethods[0] = level;
673  fSuccess[0] = 0;
674  fFailure[0] = 0;
675  if (details && strlen(details) > 0) {
676  fDetails[0] = details;
677  } else {
678  // Use default instead
679  char *tmp = TAuthenticate::GetDefaultDetails(level,0,fUser);
680  fDetails[0] = (const char *)tmp;
681  delete[] tmp;
682  }
683 
684  // Increment total number
685  fNumMethods++;
686 
687  if (gDebug > 3) Print();
688 }
689 
690 
691 ////////////////////////////////////////////////////////////////////////////////
692 /// Count successes for 'method'
693 
694 void THostAuth::CountSuccess(Int_t method)
695 {
696  int i;
697  for (i = 0; i < fNumMethods; i++) {
698  if (fMethods[i] == method) {
699  fSuccess[i]++;
700  break;
701  }
702  }
703 }
704 
705 ////////////////////////////////////////////////////////////////////////////////
706 /// Count failures for 'method'
707 
708 void THostAuth::CountFailure(Int_t method)
709 {
710  int i;
711  for (i = 0; i < fNumMethods; i++) {
712  if (fMethods[i] == method) {
713  fFailure[i]++;
714  break;
715  }
716  }
717 }
718 
719 ////////////////////////////////////////////////////////////////////////////////
720 /// Create a Security context and add it to local list
721 /// Return pointer to it to be stored in TAuthenticate
722 
723 TRootSecContext *THostAuth::CreateSecContext(const char *user, const char *host,
724  Int_t meth, Int_t offset,
725  const char *details, const char *token,
726  TDatime expdate, void *sctx, Int_t key)
727 {
728  TRootSecContext *ctx = new TRootSecContext(user, host, meth, offset, details,
729  token, expdate, sctx, key);
730  // Add it also to the local list if active
731  if (ctx->IsActive())
732  fSecContexts->Add(ctx);
733 
734  return ctx;
735 
736 }
737 
738 ////////////////////////////////////////////////////////////////////////////////
739 /// Return a static string with all info in a serialized form
740 
741 void THostAuth::AsString(TString &Out) const
742 {
743  Out = Form("h:%s u:%s n:%d",GetHost(),GetUser(),fNumMethods);
744 
745  Int_t i = 0;
746  for (; i < fNumMethods; i++) {
747  Out += TString(Form(" '%d %s'",fMethods[i],fDetails[i].Data()));
748  }
749 
750 }