nsswitch/winbind_nss_irix.c

説明を見る。
00001 /* 
00002    Unix SMB/CIFS implementation.
00003 
00004    Windows NT Domain nsswitch module
00005 
00006    Copyright (C) Tim Potter 2000
00007    Copyright (C) James Peach 2006
00008    
00009    This library is free software; you can redistribute it and/or
00010    modify it under the terms of the GNU Library General Public
00011    License as published by the Free Software Foundation; either
00012    version 2 of the License, or (at your option) any later version.
00013    
00014    This library is distributed in the hope that it will be useful,
00015    but WITHOUT ANY WARRANTY; without even the implied warranty of
00016    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017    Library General Public License for more details.
00018    
00019    You should have received a copy of the GNU Library General Public
00020    License along with this library; if not, write to the
00021    Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00022    Boston, MA  02111-1307, USA.   
00023 */
00024 
00025 #include "winbind_client.h"
00026 
00027 #ifndef PRINTF_ATTRIBUTE
00028 #define PRINTF_ATTRIBUTE(m, n)
00029 #endif
00030 
00031 #ifndef HAVE_ASPRINTF_DECL
00032 /*PRINTFLIKE2 */
00033 int asprintf(char **,const char *, ...) PRINTF_ATTRIBUTE(2,3);
00034 #endif
00035 
00036 #ifdef HAVE_NS_API_H
00037 #undef VOLATILE
00038 #undef STATIC
00039 #undef DYNAMIC
00040 #include <ns_daemon.h>
00041 #endif
00042 
00043 /* Maximum number of users to pass back over the unix domain socket
00044    per call. This is not a static limit on the total number of users 
00045    or groups returned in total. */
00046 
00047 #define MAX_GETPWENT_USERS 250
00048 #define MAX_GETGRENT_USERS 250
00049 
00050 /* Prototypes from wb_common.c */
00051 
00052 extern int winbindd_fd;
00053 
00054 #ifdef HAVE_NS_API_H
00055 
00056 /* IRIX version */
00057 
00058 static int send_next_request(nsd_file_t *, struct winbindd_request *);
00059 static int do_list(int state, nsd_file_t *rq);
00060 
00061 static nsd_file_t *current_rq = NULL;
00062 static int current_winbind_xid = 0;
00063 static int next_winbind_xid = 0;
00064 
00065 typedef struct winbind_xid {
00066         int                     xid;
00067         nsd_file_t              *rq;
00068         struct winbindd_request *request;
00069         struct winbind_xid      *next;
00070 } winbind_xid_t;
00071 
00072 static winbind_xid_t *winbind_xids = (winbind_xid_t *)0;
00073 
00074 static int
00075 winbind_xid_new(int xid, nsd_file_t *rq, struct winbindd_request *request)
00076 {
00077         winbind_xid_t *new;
00078 
00079         nsd_logprintf(NSD_LOG_LOW,
00080                 "entering winbind_xid_new xid = %d rq = 0x%x, request = 0x%x\n",
00081                 xid, rq, request);
00082         new = (winbind_xid_t *)nsd_calloc(1,sizeof(winbind_xid_t));
00083         if (!new) {
00084                 nsd_logprintf(NSD_LOG_RESOURCE,"winbind_xid_new: failed malloc\n");
00085                 return NSD_ERROR;
00086         }
00087 
00088         new->xid = xid;
00089         new->rq = rq;
00090         new->request = request;
00091         new->next = winbind_xids;
00092         winbind_xids = new;
00093 
00094         return NSD_CONTINUE;
00095 }
00096 
00097 /*
00098 ** This routine will look down the xid list and return the request
00099 ** associated with an xid.  We remove the record if it is found.
00100 */
00101 nsd_file_t *
00102 winbind_xid_lookup(int xid, struct winbindd_request **requestp)
00103 {
00104         winbind_xid_t **last, *dx;
00105         nsd_file_t *result=0;
00106 
00107         for (last = &winbind_xids, dx = winbind_xids; dx && (dx->xid != xid);
00108             last = &dx->next, dx = dx->next);
00109         if (dx) {
00110                 *last = dx->next;
00111                 result = dx->rq;
00112                 *requestp = dx->request;
00113                 SAFE_FREE(dx);
00114         }
00115         nsd_logprintf(NSD_LOG_LOW,
00116                 "entering winbind_xid_lookup xid = %d rq = 0x%x, request = 0x%x\n",
00117                 xid, result, dx->request);
00118 
00119         return result;
00120 }
00121 
00122 static int
00123 winbind_startnext_timeout(nsd_file_t **rqp, nsd_times_t *to)
00124 {
00125         nsd_file_t *rq;
00126         struct winbindd_request *request;
00127 
00128         nsd_logprintf(NSD_LOG_MIN, "timeout (winbind startnext)\n");
00129         rq = to->t_file;
00130         *rqp = rq;
00131         nsd_timeout_remove(rq);
00132         request = to->t_clientdata;
00133         return(send_next_request(rq, request));
00134 }
00135 
00136 static void
00137 dequeue_request(void)
00138 {
00139         nsd_file_t *rq;
00140         struct winbindd_request *request;
00141 
00142         /*
00143          * Check for queued requests
00144          */
00145         if (winbind_xids) {
00146             nsd_logprintf(NSD_LOG_MIN, "timeout (winbind) unqueue xid %d\n",
00147                         current_winbind_xid);
00148             rq = winbind_xid_lookup(current_winbind_xid++, &request);
00149             /* cause a timeout on the queued request so we can send it */
00150             nsd_timeout_new(rq,1,winbind_startnext_timeout,request);
00151         }
00152 }
00153 
00154 static int
00155 do_request(nsd_file_t *rq, struct winbindd_request *request)
00156 {
00157         if (winbind_xids == NULL) {
00158                 /*
00159                  * No outstanding requests.
00160                  * Send off the request to winbindd
00161                  */
00162                 nsd_logprintf(NSD_LOG_MIN, "lookup (winbind) sending request\n");
00163                 return(send_next_request(rq, request));
00164         } else {
00165                 /*
00166                  * Just queue it up for now - previous callout or timout
00167                  * will start it up
00168                  */
00169                 nsd_logprintf(NSD_LOG_MIN,
00170                         "lookup (winbind): queue request xid = %d\n",
00171                         next_winbind_xid);
00172                 return(winbind_xid_new(next_winbind_xid++, rq, request));
00173         }
00174 }
00175 
00176 static int 
00177 winbind_callback(nsd_file_t **rqp, int fd)
00178 {
00179         struct winbindd_response response;
00180         nsd_file_t *rq;
00181         NSS_STATUS status;
00182         char * result = NULL;
00183         size_t rlen;
00184 
00185         dequeue_request();
00186 
00187         nsd_logprintf(NSD_LOG_MIN, "entering callback (winbind)\n");
00188 
00189         rq = current_rq;
00190         *rqp = rq;
00191 
00192         nsd_timeout_remove(rq);
00193         nsd_callback_remove(fd);
00194 
00195         ZERO_STRUCT(response);
00196         status = winbindd_get_response(&response);
00197 
00198         if (status != NSS_STATUS_SUCCESS) {
00199                 /* free any extra data area in response structure */
00200                 free_response(&response);
00201                 nsd_logprintf(NSD_LOG_MIN, 
00202                         "callback (winbind) returning not found, status = %d\n",
00203                         status);
00204 
00205                 switch (status) {
00206                     case NSS_STATUS_UNAVAIL:
00207                         rq->f_status = NS_UNAVAIL;
00208                         break;
00209                     case NSS_STATUS_TRYAGAIN:
00210                         rq->f_status = NS_TRYAGAIN;
00211                         break;
00212                     case NSS_STATUS_NOTFOUND:
00213                         /* FALLTHRU */
00214                     default:
00215                         rq->f_status = NS_NOTFOUND;
00216                 }
00217 
00218                 return NSD_NEXT;
00219         }
00220 
00221         switch ((int)rq->f_cmd_data) {
00222             case WINBINDD_WINS_BYNAME:
00223             case WINBINDD_WINS_BYIP:
00224                 nsd_logprintf(NSD_LOG_MIN,
00225                         "callback (winbind) WINS_BYNAME | WINS_BYIP\n");
00226 
00227                 rlen = asprintf(&result, "%s\n", response.data.winsresp);
00228                 if (rlen == 0 || result == NULL) {
00229                         return NSD_ERROR;
00230                 }
00231                 
00232                 free_response(&response);
00233                 
00234                 nsd_logprintf(NSD_LOG_MIN, "    %s\n", result);
00235                 nsd_set_result(rq, NS_SUCCESS, result, rlen, DYNAMIC);
00236                 return NSD_OK;
00237 
00238             case WINBINDD_GETPWUID:
00239             case WINBINDD_GETPWNAM:
00240             {
00241                 struct winbindd_pw *pw = &response.data.pw;
00242             
00243                 nsd_logprintf(NSD_LOG_MIN,
00244                         "callback (winbind) GETPWUID | GETPWUID\n");
00245 
00246                 rlen = asprintf(&result,"%s:%s:%d:%d:%s:%s:%s\n",
00247                                 pw->pw_name,
00248                                 pw->pw_passwd,
00249                                 pw->pw_uid,
00250                                 pw->pw_gid,
00251                                 pw->pw_gecos,
00252                                 pw->pw_dir,
00253                                 pw->pw_shell);
00254                 if (rlen == 0 || result == NULL)
00255                     return NSD_ERROR;
00256             
00257                 free_response(&response);
00258             
00259                 nsd_logprintf(NSD_LOG_MIN, "    %s\n", result);
00260                 nsd_set_result(rq, NS_SUCCESS, result, rlen, DYNAMIC);
00261                 return NSD_OK;
00262             }
00263 
00264             case WINBINDD_GETGRNAM:
00265             case WINBINDD_GETGRGID:
00266             {
00267                 const struct winbindd_gr *gr = &response.data.gr;
00268                 const char * members;
00269             
00270                 nsd_logprintf(NSD_LOG_MIN,
00271                         "callback (winbind) GETGRNAM | GETGRGID\n");
00272 
00273                 if (gr->num_gr_mem && response.extra_data.data) {
00274                         members = response.extra_data.data;
00275                 } else {
00276                         members = "";
00277                 }
00278             
00279                 rlen = asprintf(&result, "%s:%s:%d:%s\n",
00280                             gr->gr_name, gr->gr_passwd, gr->gr_gid, members);
00281                 if (rlen == 0 || result == NULL)
00282                     return NSD_ERROR;
00283             
00284                 free_response(&response);
00285             
00286                 nsd_logprintf(NSD_LOG_MIN, "    %s\n", result);
00287                 nsd_set_result(rq, NS_SUCCESS, result, rlen, DYNAMIC);
00288                 return NSD_OK;
00289             }
00290 
00291             case WINBINDD_SETGRENT:
00292             case WINBINDD_SETPWENT:
00293                 nsd_logprintf(NSD_LOG_MIN,
00294                         "callback (winbind) SETGRENT | SETPWENT\n");
00295                 free_response(&response);
00296                 return(do_list(1,rq));
00297 
00298             case WINBINDD_GETGRENT:
00299             case WINBINDD_GETGRLST:
00300             {
00301                 int entries;
00302             
00303                 nsd_logprintf(NSD_LOG_MIN,
00304                     "callback (winbind) GETGRENT | GETGRLIST %d responses\n",
00305                     response.data.num_entries);
00306             
00307                 if (response.data.num_entries) {
00308                     const struct winbindd_gr *gr = &response.data.gr;
00309                     const char * members;
00310                     fstring grp_name;
00311                     int     i;
00312             
00313                     gr = (struct winbindd_gr *)response.extra_data.data;
00314                     if (! gr ) {
00315                         nsd_logprintf(NSD_LOG_MIN, "     no extra_data\n");
00316                         free_response(&response);
00317                         return NSD_ERROR;
00318                     }
00319             
00320                     members = (char *)response.extra_data.data +
00321                         (response.data.num_entries * sizeof(struct winbindd_gr));
00322             
00323                     for (i = 0; i < response.data.num_entries; i++) {
00324                         snprintf(grp_name, sizeof(grp_name) - 1, "%s:%s:%d:",
00325                                     gr->gr_name, gr->gr_passwd, gr->gr_gid);
00326             
00327                         nsd_append_element(rq, NS_SUCCESS, result, rlen);
00328                         nsd_append_result(rq, NS_SUCCESS,
00329                                 &members[gr->gr_mem_ofs],
00330                                 strlen(&members[gr->gr_mem_ofs]));
00331             
00332                         /* Don't log the whole list, because it might be
00333                          * _really_ long and we probably don't want to clobber
00334                          * the log with it.
00335                          */
00336                         nsd_logprintf(NSD_LOG_MIN, "    %s (...)\n", grp_name);
00337             
00338                         gr++;
00339                     }
00340                 }
00341             
00342                 entries = response.data.num_entries;
00343                 free_response(&response);
00344                 if (entries < MAX_GETPWENT_USERS)
00345                     return(do_list(2,rq));
00346                 else
00347                     return(do_list(1,rq));
00348             }
00349 
00350             case WINBINDD_GETPWENT:
00351             {
00352                 int entries;
00353 
00354                 nsd_logprintf(NSD_LOG_MIN,
00355                         "callback (winbind) GETPWENT  %d responses\n",
00356                         response.data.num_entries);
00357 
00358                 if (response.data.num_entries) {
00359                     struct winbindd_pw *pw = &response.data.pw;
00360                     int i;
00361 
00362                     pw = (struct winbindd_pw *)response.extra_data.data;
00363                     if (! pw ) {
00364                         nsd_logprintf(NSD_LOG_MIN, "     no extra_data\n");
00365                         free_response(&response);
00366                         return NSD_ERROR;
00367                     }
00368                     for (i = 0; i < response.data.num_entries; i++) {
00369                         result = NULL;
00370                         rlen = asprintf(&result, "%s:%s:%d:%d:%s:%s:%s",
00371                                         pw->pw_name,
00372                                         pw->pw_passwd,
00373                                         pw->pw_uid,
00374                                         pw->pw_gid,
00375                                         pw->pw_gecos,
00376                                         pw->pw_dir,
00377                                         pw->pw_shell);
00378 
00379                         if (rlen != 0 && result != NULL) {
00380                             nsd_logprintf(NSD_LOG_MIN, "    %s\n",result);
00381                             nsd_append_element(rq, NS_SUCCESS, result, rlen);
00382                             free(result);
00383                         }
00384 
00385                         pw++;
00386                     }
00387                 }
00388 
00389                 entries = response.data.num_entries;
00390                 free_response(&response);
00391                 if (entries < MAX_GETPWENT_USERS)
00392                     return(do_list(2,rq));
00393                 else
00394                     return(do_list(1,rq));
00395             }
00396 
00397             case WINBINDD_ENDGRENT:
00398             case WINBINDD_ENDPWENT:
00399                 nsd_logprintf(NSD_LOG_MIN, "callback (winbind) ENDGRENT | ENDPWENT\n");
00400                 nsd_append_element(rq, NS_SUCCESS, "\n", 1);
00401                 free_response(&response);
00402                 return NSD_NEXT;
00403 
00404             default:
00405                 free_response(&response);
00406                 nsd_logprintf(NSD_LOG_MIN, "callback (winbind) invalid command %d\n", (int)rq->f_cmd_data);
00407                 return NSD_NEXT;
00408         }
00409 }
00410 
00411 static int 
00412 winbind_timeout(nsd_file_t **rqp, nsd_times_t *to)
00413 {
00414         nsd_file_t *rq;
00415 
00416         dequeue_request();
00417 
00418         nsd_logprintf(NSD_LOG_MIN, "timeout (winbind)\n");
00419 
00420         rq = to->t_file;
00421         *rqp = rq;
00422 
00423         /* Remove the callback and timeout */
00424         nsd_callback_remove(winbindd_fd);
00425         nsd_timeout_remove(rq);
00426 
00427         rq->f_status = NS_NOTFOUND;
00428         return NSD_NEXT;
00429 }
00430 
00431 static int
00432 send_next_request(nsd_file_t *rq, struct winbindd_request *request)
00433 {
00434         NSS_STATUS status;
00435         long timeout;
00436 
00437         switch (rq->f_index) {
00438                 case LOOKUP:
00439                         timeout = nsd_attr_fetch_long(rq->f_attrs,
00440                                         "lookup_timeout", 10, 10);
00441                         break;
00442                 case LIST:
00443                         timeout = nsd_attr_fetch_long(rq->f_attrs,
00444                                         "list_timeout", 10, 10);
00445                         break;
00446                 default:
00447                         nsd_logprintf(NSD_LOG_OPER,
00448                                 "send_next_request (winbind) "
00449                                 "invalid request type %d\n", rq->f_index);
00450                         rq->f_status = NS_BADREQ;
00451                         return NSD_NEXT;
00452         }
00453 
00454         nsd_logprintf(NSD_LOG_MIN,
00455                 "send_next_request (winbind) %d, timeout = %d sec\n",
00456                         rq->f_cmd_data, timeout);
00457         status = winbindd_send_request((int)rq->f_cmd_data,0,request);
00458         SAFE_FREE(request);
00459 
00460         if (status != NSS_STATUS_SUCCESS) {
00461                 nsd_logprintf(NSD_LOG_MIN, 
00462                         "send_next_request (winbind) error status = %d\n",
00463                         status);
00464                 rq->f_status = status;
00465                 return NSD_NEXT;
00466         }
00467 
00468         current_rq = rq;
00469 
00470         /*
00471          * Set up callback and timeouts
00472          */
00473         nsd_logprintf(NSD_LOG_MIN, "send_next_request (winbind) fd = %d\n",
00474                 winbindd_fd);
00475 
00476         nsd_callback_new(winbindd_fd, winbind_callback, NSD_READ);
00477         nsd_timeout_new(rq, timeout * 1000, winbind_timeout, NULL);
00478         return NSD_CONTINUE;
00479 }
00480 
00481 int init(void)
00482 {
00483         nsd_logprintf(NSD_LOG_MIN, "entering init (winbind)\n");
00484         return(NSD_OK);
00485 }
00486 
00487 int lookup(nsd_file_t *rq)
00488 {
00489         char *map;
00490         char *key;
00491         struct winbindd_request *request;
00492 
00493         nsd_logprintf(NSD_LOG_MIN, "entering lookup (winbind)\n");
00494         if (! rq)
00495                 return NSD_ERROR;
00496 
00497         map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0);
00498         key = nsd_attr_fetch_string(rq->f_attrs, "key", (char*)0);
00499         if (! map || ! key) {
00500                 nsd_logprintf(NSD_LOG_MIN, "lookup (winbind) table or key not defined\n");
00501                 rq->f_status = NS_BADREQ;
00502                 return NSD_ERROR;
00503         }
00504 
00505         nsd_logprintf(NSD_LOG_MIN, "lookup (winbind %s)\n",map);
00506 
00507         request = (struct winbindd_request *)nsd_calloc(1,sizeof(struct winbindd_request));
00508         if (! request) {
00509                 nsd_logprintf(NSD_LOG_RESOURCE,
00510                         "lookup (winbind): failed malloc\n");
00511                 return NSD_ERROR;
00512         }
00513 
00514         if (strcasecmp(map,"passwd.byuid") == 0) {
00515             request->data.uid = atoi(key);
00516             rq->f_cmd_data = (void *)WINBINDD_GETPWUID;
00517         } else if (strcasecmp(map,"passwd.byname") == 0) {
00518             strncpy(request->data.username, key, 
00519                 sizeof(request->data.username) - 1);
00520             request->data.username[sizeof(request->data.username) - 1] = '\0';
00521             rq->f_cmd_data = (void *)WINBINDD_GETPWNAM; 
00522         } else if (strcasecmp(map,"group.byname") == 0) {
00523             strncpy(request->data.groupname, key, 
00524                 sizeof(request->data.groupname) - 1);
00525             request->data.groupname[sizeof(request->data.groupname) - 1] = '\0';
00526             rq->f_cmd_data = (void *)WINBINDD_GETGRNAM; 
00527         } else if (strcasecmp(map,"group.bygid") == 0) {
00528             request->data.gid = atoi(key);
00529             rq->f_cmd_data = (void *)WINBINDD_GETGRGID;
00530         } else if (strcasecmp(map,"hosts.byname") == 0) {
00531             strncpy(request->data.winsreq, key, sizeof(request->data.winsreq) - 1);
00532             request->data.winsreq[sizeof(request->data.winsreq) - 1] = '\0';
00533             rq->f_cmd_data = (void *)WINBINDD_WINS_BYNAME;
00534         } else if (strcasecmp(map,"hosts.byaddr") == 0) {
00535             strncpy(request->data.winsreq, key, sizeof(request->data.winsreq) - 1);
00536             request->data.winsreq[sizeof(request->data.winsreq) - 1] = '\0';
00537             rq->f_cmd_data = (void *)WINBINDD_WINS_BYIP;
00538         } else {
00539                 /*
00540                  * Don't understand this map - just return not found
00541                  */
00542                 nsd_logprintf(NSD_LOG_MIN, "lookup (winbind) unknown table\n");
00543                 SAFE_FREE(request);
00544                 rq->f_status = NS_NOTFOUND;
00545                 return NSD_NEXT;
00546         }
00547 
00548         return(do_request(rq, request));
00549 }
00550 
00551 int list(nsd_file_t *rq)
00552 {
00553         char *map;
00554 
00555         nsd_logprintf(NSD_LOG_MIN, "entering list (winbind)\n");
00556         if (! rq)
00557                 return NSD_ERROR;
00558 
00559         map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0);
00560         if (! map ) {
00561                 nsd_logprintf(NSD_LOG_MIN, "list (winbind) table not defined\n");
00562                 rq->f_status = NS_BADREQ;
00563                 return NSD_ERROR;
00564         }
00565 
00566         nsd_logprintf(NSD_LOG_MIN, "list (winbind %s)\n",map);
00567 
00568         return (do_list(0,rq));
00569 }
00570 
00571 static int
00572 do_list(int state, nsd_file_t *rq)
00573 {
00574         char *map;
00575         struct winbindd_request *request;
00576 
00577         nsd_logprintf(NSD_LOG_MIN, "entering do_list (winbind) state = %d\n",state);
00578 
00579         map = nsd_attr_fetch_string(rq->f_attrs, "table", (char*)0);
00580         request = (struct winbindd_request *)nsd_calloc(1,sizeof(struct winbindd_request));
00581         if (! request) {
00582                 nsd_logprintf(NSD_LOG_RESOURCE,
00583                         "do_list (winbind): failed malloc\n");
00584                 return NSD_ERROR;
00585         }
00586 
00587         if (strcasecmp(map,"passwd.byname") == 0) {
00588             switch (state) {
00589                 case 0:
00590                     rq->f_cmd_data = (void *)WINBINDD_SETPWENT;
00591                     break;
00592                 case 1:
00593                     request->data.num_entries = MAX_GETPWENT_USERS;
00594                     rq->f_cmd_data = (void *)WINBINDD_GETPWENT;
00595                     break;
00596                 case 2:
00597                     rq->f_cmd_data = (void *)WINBINDD_ENDPWENT;
00598                     break;
00599                 default:
00600                     nsd_logprintf(NSD_LOG_MIN, "do_list (winbind) unknown state\n");
00601                     SAFE_FREE(request);
00602                     rq->f_status = NS_NOTFOUND;
00603                     return NSD_NEXT;
00604             }
00605         } else if (strcasecmp(map,"group.byname") == 0) {
00606             switch (state) {
00607                 case 0:
00608                     rq->f_cmd_data = (void *)WINBINDD_SETGRENT;
00609                     break;
00610                 case 1:
00611                     request->data.num_entries = MAX_GETGRENT_USERS;
00612                     rq->f_cmd_data = (void *)WINBINDD_GETGRENT;
00613                     break;
00614                 case 2:
00615                     rq->f_cmd_data = (void *)WINBINDD_ENDGRENT;
00616                     break;
00617                 default:
00618                     nsd_logprintf(NSD_LOG_MIN, "do_list (winbind) unknown state\n");
00619                     SAFE_FREE(request);
00620                     rq->f_status = NS_NOTFOUND;
00621                     return NSD_NEXT;
00622             }
00623         } else {
00624                 /*
00625                  * Don't understand this map - just return not found
00626                  */
00627                 nsd_logprintf(NSD_LOG_MIN, "do_list (winbind) unknown table\n");
00628                 SAFE_FREE(request);
00629                 rq->f_status = NS_NOTFOUND;
00630                 return NSD_NEXT;
00631         }
00632 
00633         return(do_request(rq, request));
00634 }
00635 
00636 #endif /* HAVE_NS_API_H */

Sambaに対してSat Aug 29 21:23:09 2009に生成されました。  doxygen 1.4.7